blob: f056d2bf806de6b338eac0ca28ef4621780a60c9 [file] [log] [blame]
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Blit.cpp: Surface copy utility class.
8
daniel@transgaming.combbf56f72010-04-20 18:52:13 +00009#include "libGLESv2/Blit.h"
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000010
11#include <d3dx9.h>
12
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000013#include "common/debug.h"
14
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000015#include "libGLESv2/main.h"
16
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000017namespace
18{
19// Standard Vertex Shader
20// Input 0 is the homogenous position.
21// Outputs the homogenous position as-is.
22// Outputs a tex coord with (0,0) in the upper-left corner of the screen and (1,1) in the bottom right.
23// C0.X must be negative half-pixel width, C0.Y must be half-pixel height. C0.ZW must be 0.
24const char standardvs[] =
25"struct VS_OUTPUT\n"
26"{\n"
27" float4 position : POSITION;\n"
28" float4 texcoord : TEXCOORD0;\n"
29"};\n"
30"\n"
31"uniform float4 halfPixelSize : c0;\n"
32"\n"
33"VS_OUTPUT main(in float4 position : POSITION)\n"
34"{\n"
35" VS_OUTPUT Out;\n"
36"\n"
37" Out.position = position + halfPixelSize;\n"
38" Out.texcoord = position * float4(0.5, -0.5, 1.0, 1.0) + float4(0.5, 0.5, 0, 0);\n"
39"\n"
40" return Out;\n"
41"}\n";
42
43// Flip Y Vertex Shader
44// Input 0 is the homogenous position.
45// Outputs the homogenous position as-is.
46// Outputs a tex coord with (0,1) in the upper-left corner of the screen and (1,0) in the bottom right.
47// C0.XY must be the half-pixel width and height. C0.ZW must be 0.
48const char flipyvs[] =
49"struct VS_OUTPUT\n"
50"{\n"
51" float4 position : POSITION;\n"
52" float4 texcoord : TEXCOORD0;\n"
53"};\n"
54"\n"
55"uniform float4 halfPixelSize : c0;\n"
56"\n"
57"VS_OUTPUT main(in float4 position : POSITION)\n"
58"{\n"
59" VS_OUTPUT Out;\n"
60"\n"
61" Out.position = position + halfPixelSize;\n"
62" Out.texcoord = position * float4(0.5, 0.5, 1.0, 1.0) + float4(0.5, 0.5, 0, 0);\n"
63"\n"
64" return Out;\n"
65"}\n";
66
67// Passthrough Pixel Shader
68// Outputs texture 0 sampled at texcoord 0.
69const char passthroughps[] =
70"sampler2D tex : s0;\n"
71"\n"
72"float4 main(float4 texcoord : TEXCOORD0) : COLOR\n"
73"{\n"
74" return tex2D(tex, texcoord.xy);\n"
75"}\n";
76
77// Luminance Conversion Pixel Shader
78// Outputs sample(tex0, tc0).rrra.
79// For LA output (pass A) set C0.X = 1, C0.Y = 0.
80// For L output (A = 1) set C0.X = 0, C0.Y = 1.
81const char luminanceps[] =
82"sampler2D tex : s0;\n"
83"\n"
84"uniform float4 mode : c0;\n"
85"\n"
86"float4 main(float4 texcoord : TEXCOORD0) : COLOR\n"
87"{\n"
88" float4 tmp = tex2D(tex, texcoord.xy);\n"
89" tmp.w = tmp.w * mode.x + mode.y;\n"
90" return tmp.xxxw;\n"
91"}\n";
92
93// RGB/A Component Mask Pixel Shader
94// Outputs sample(tex0, tc0) with options to force RGB = 0 and/or A = 1.
95// To force RGB = 0, set C0.X = 0, otherwise C0.X = 1.
96// To force A = 1, set C0.Z = 0, C0.W = 1, otherwise C0.Z = 1, C0.W = 0.
97const char componentmaskps[] =
98"sampler2D tex : s0;\n"
99"\n"
100"uniform float4 mode : c0;\n"
101"\n"
102"float4 main(float4 texcoord : TEXCOORD0) : COLOR\n"
103"{\n"
104" float4 tmp = tex2D(tex, texcoord.xy);\n"
105" tmp.xyz = tmp.xyz * mode.x;\n"
106" tmp.w = tmp.w * mode.z + mode.w;\n"
107" return tmp;\n"
108"}\n";
109
110}
111
112namespace gl
113{
114
115const char * const Blit::mShaderSource[] =
116{
117 standardvs,
118 flipyvs,
119 passthroughps,
120 luminanceps,
121 componentmaskps
122};
123
124Blit::Blit(Context *context)
125 : mContext(context), mQuadVertexBuffer(NULL), mQuadVertexDeclaration(NULL)
126{
127 initGeometry();
128 memset(mCompiledShaders, 0, sizeof(mCompiledShaders));
129}
130
131Blit::~Blit()
132{
133 if (mQuadVertexBuffer) mQuadVertexBuffer->Release();
134 if (mQuadVertexDeclaration) mQuadVertexDeclaration->Release();
135
136 for (int i = 0; i < SHADER_COUNT; i++)
137 {
138 if (mCompiledShaders[i])
139 {
140 mCompiledShaders[i]->Release();
141 }
142 }
143}
144
145void Blit::initGeometry()
146{
147 static const float quad[] =
148 {
149 -1, -1,
150 -1, 1,
151 1, -1,
152 1, 1
153 };
154
155 IDirect3DDevice9 *device = getDevice();
156
157 HRESULT hr = device->CreateVertexBuffer(sizeof(quad), D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &mQuadVertexBuffer, NULL);
158
159 if (FAILED(hr))
160 {
161 ASSERT(hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
162 return error(GL_OUT_OF_MEMORY);
163 }
164
165 void *lockPtr;
166 mQuadVertexBuffer->Lock(0, 0, &lockPtr, 0);
167 memcpy(lockPtr, quad, sizeof(quad));
168 mQuadVertexBuffer->Unlock();
169
170 static const D3DVERTEXELEMENT9 elements[] =
171 {
172 { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
173 D3DDECL_END()
174 };
175
176 hr = device->CreateVertexDeclaration(elements, &mQuadVertexDeclaration);
177 if (FAILED(hr))
178 {
179 ASSERT(hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
180 return error(GL_OUT_OF_MEMORY);
181 }
182}
183
184template <class D3DShaderType>
185bool Blit::setShader(ShaderId source, const char *profile,
186 HRESULT (WINAPI IDirect3DDevice9::*createShader)(const DWORD *, D3DShaderType**),
187 HRESULT (WINAPI IDirect3DDevice9::*setShader)(D3DShaderType*))
188{
189 IDirect3DDevice9 *device = getDevice();
190
191 D3DShaderType *shader;
192
193 if (mCompiledShaders[source] != NULL)
194 {
195 shader = static_cast<D3DShaderType*>(mCompiledShaders[source]);
196 }
197 else
198 {
199 ID3DXBuffer *shaderCode;
200 HRESULT hr = D3DXCompileShader(mShaderSource[source], strlen(mShaderSource[source]), NULL, NULL, "main", profile, 0, &shaderCode, NULL, NULL);
201
202 if (FAILED(hr))
203 {
204 ERR("Failed to compile %s shader for blit operation %d, error 0x%08X.", profile, (int)source, hr);
205 return false;
206 }
207
208 hr = (device->*createShader)(static_cast<const DWORD*>(shaderCode->GetBufferPointer()), &shader);
209 if (FAILED(hr))
210 {
211 shaderCode->Release();
212 ERR("Failed to create %s shader for blit operation %d, error 0x%08X.", profile, (int)source, hr);
213 return false;
214 }
215
216 shaderCode->Release();
217
218 mCompiledShaders[source] = shader;
219 }
220
221 HRESULT hr = (device->*setShader)(shader);
222
223 if (FAILED(hr))
224 {
225 ERR("Failed to set %s shader for blit operation %d, error 0x%08X.", profile, (int)source, hr);
226 return false;
227 }
228
229 return true;
230}
231
232bool Blit::setVertexShader(ShaderId shader)
233{
234 return setShader<IDirect3DVertexShader9>(shader, mContext->getVertexShaderProfile(), &IDirect3DDevice9::CreateVertexShader, &IDirect3DDevice9::SetVertexShader);
235}
236
237bool Blit::setPixelShader(ShaderId shader)
238{
239 return setShader<IDirect3DPixelShader9>(shader, mContext->getPixelShaderProfile(), &IDirect3DDevice9::CreatePixelShader, &IDirect3DDevice9::SetPixelShader);
240}
241
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000242RECT Blit::getSurfaceRect(IDirect3DSurface9 *surface) const
243{
244 D3DSURFACE_DESC desc;
245 surface->GetDesc(&desc);
246
247 RECT rect;
248 rect.left = 0;
249 rect.top = 0;
250 rect.right = desc.Width;
251 rect.bottom = desc.Height;
252
253 return rect;
254}
255
256bool Blit::boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest)
257{
258 IDirect3DTexture9 *texture = copySurfaceToTexture(source, getSurfaceRect(source));
259 if (!texture)
260 {
261 return false;
262 }
263
264 IDirect3DDevice9 *device = getDevice();
265 device->SetTexture(0, texture);
266 device->SetRenderTarget(0, dest);
267
268 setVertexShader(SHADER_VS_STANDARD);
269 setPixelShader(SHADER_PS_PASSTHROUGH);
270
271 setCommonBlitState();
272 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
273 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
274
275 setViewport(getSurfaceRect(dest), 0, 0);
276
277 render();
278
279 texture->Release();
280
281 return true;
282}
283
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000284bool Blit::formatConvert(IDirect3DSurface9 *source, const RECT &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, IDirect3DSurface9 *dest)
285{
286 IDirect3DTexture9 *texture = copySurfaceToTexture(source, sourceRect);
287 if (!texture)
288 {
289 return false;
290 }
291
292 IDirect3DDevice9 *device = getDevice();
293
294 device->SetTexture(0, texture);
295 device->SetRenderTarget(0, dest);
296
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000297 setViewport(sourceRect, xoffset, yoffset);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000298
299 setCommonBlitState();
300 if (setFormatConvertShaders(destFormat))
301 {
302 render();
303 }
304
305 texture->Release();
306
307 return true;
308}
309
310bool Blit::setFormatConvertShaders(GLenum destFormat)
311{
312 bool okay = setVertexShader(SHADER_VS_STANDARD);
313
314 switch (destFormat)
315 {
316 default: UNREACHABLE();
317 case GL_RGBA:
318 case GL_RGB:
319 case GL_ALPHA:
320 okay = okay && setPixelShader(SHADER_PS_COMPONENTMASK);
321 break;
322
323 case GL_LUMINANCE:
324 case GL_LUMINANCE_ALPHA:
325 okay = okay && setPixelShader(SHADER_PS_LUMINANCE);
326 break;
327 }
328
329 if (!okay)
330 {
331 return false;
332 }
333
334 enum { X = 0, Y = 1, Z = 2, W = 3 };
335
336 // The meaning of this constant depends on the shader that was selected.
337 // See the shader assembly code above for details.
338 float psConst0[4] = { 0, 0, 0, 0 };
339
340 switch (destFormat)
341 {
342 default: UNREACHABLE();
343 case GL_RGBA:
344 psConst0[X] = 1;
345 psConst0[Z] = 1;
346 break;
347
348 case GL_RGB:
349 psConst0[X] = 1;
350 psConst0[W] = 1;
351 break;
352
353 case GL_ALPHA:
354 psConst0[Z] = 1;
355 break;
356
357 case GL_LUMINANCE:
358 psConst0[Y] = 1;
359 break;
360
361 case GL_LUMINANCE_ALPHA:
362 psConst0[X] = 1;
363 break;
364 }
365
366 getDevice()->SetPixelShaderConstantF(0, psConst0, 1);
367
368 return true;
369}
370
371IDirect3DTexture9 *Blit::copySurfaceToTexture(IDirect3DSurface9 *surface, const RECT &sourceRect)
372{
373 IDirect3DDevice9 *device = getDevice();
374
375 D3DSURFACE_DESC sourceDesc;
376 surface->GetDesc(&sourceDesc);
377
378 // Copy the render target into a texture
379 IDirect3DTexture9 *texture;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000380 HRESULT result = device->CreateTexture(sourceRect.right - sourceRect.left, sourceRect.bottom - sourceRect.top, 1, D3DUSAGE_RENDERTARGET, sourceDesc.Format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000381
382 if (FAILED(result))
383 {
384 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
385 return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
386 }
387
388 IDirect3DSurface9 *textureSurface;
389 result = texture->GetSurfaceLevel(0, &textureSurface);
390
391 if (FAILED(result))
392 {
393 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
394 texture->Release();
395 return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
396 }
397
398 RECT d3dSourceRect;
399 d3dSourceRect.left = sourceRect.left;
400 d3dSourceRect.right = sourceRect.right;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000401 d3dSourceRect.top = sourceRect.top;
402 d3dSourceRect.bottom = sourceRect.bottom;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000403
404 result = device->StretchRect(surface, &d3dSourceRect, textureSurface, NULL, D3DTEXF_NONE);
405
406 textureSurface->Release();
407
408 if (FAILED(result))
409 {
410 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
411 texture->Release();
412 return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
413 }
414
415 return texture;
416}
417
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000418void Blit::setViewport(const RECT &sourceRect, GLint xoffset, GLint yoffset)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000419{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000420 IDirect3DDevice9 *device = getDevice();
421
422 D3DVIEWPORT9 vp;
423 vp.X = xoffset;
424 vp.Y = yoffset;
425 vp.Width = sourceRect.right - sourceRect.left;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000426 vp.Height = sourceRect.bottom - sourceRect.top;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000427 vp.MinZ = 0.0f;
428 vp.MaxZ = 1.0f;
429 device->SetViewport(&vp);
430
431 float halfPixelAdjust[4] = { -1.0f/vp.Width, 1.0f/vp.Height, 0, 0 };
432 device->SetVertexShaderConstantF(0, halfPixelAdjust, 1);
433}
434
435void Blit::setCommonBlitState()
436{
437 IDirect3DDevice9 *device = getDevice();
438
439 device->SetDepthStencilSurface(NULL);
440
441 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
442 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
443 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
444 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
445 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
446 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
447 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
448 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
449
450 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
451 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
452 device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE);
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000453 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
454 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000455}
456
457void Blit::render()
458{
459 IDirect3DDevice9 *device = getDevice();
460
461 HRESULT hr = device->SetStreamSource(0, mQuadVertexBuffer, 0, 2 * sizeof(float));
462 hr = device->SetVertexDeclaration(mQuadVertexDeclaration);
463
464 device->BeginScene();
465 hr = device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
466 device->EndScene();
467}
468
469}