1 //
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 
9 #include "libGLESv2/Blit.h"
10 
11 #include <d3dx9.h>
12 
13 #include "common/debug.h"
14 
15 #include "libGLESv2/main.h"
16 
17 namespace
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.
24 const 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.
48 const 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.
69 const 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.
81 const 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.
97 const 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 
112 namespace gl
113 {
114 
115 const char * const Blit::mShaderSource[] =
116 {
117     standardvs,
118     flipyvs,
119     passthroughps,
120     luminanceps,
121     componentmaskps
122 };
123 
Blit(Context * context)124 Blit::Blit(Context *context)
125   : mContext(context), mQuadVertexBuffer(NULL), mQuadVertexDeclaration(NULL), mSavedRenderTarget(NULL), mSavedDepthStencil(NULL), mSavedStateBlock(NULL)
126 {
127     initGeometry();
128     memset(mCompiledShaders, 0, sizeof(mCompiledShaders));
129 }
130 
~Blit()131 Blit::~Blit()
132 {
133     if (mSavedStateBlock) mSavedStateBlock->Release();
134     if (mQuadVertexBuffer) mQuadVertexBuffer->Release();
135     if (mQuadVertexDeclaration) mQuadVertexDeclaration->Release();
136 
137     for (int i = 0; i < SHADER_COUNT; i++)
138     {
139         if (mCompiledShaders[i])
140         {
141             mCompiledShaders[i]->Release();
142         }
143     }
144 }
145 
initGeometry()146 void Blit::initGeometry()
147 {
148     static const float quad[] =
149     {
150         -1, -1,
151         -1,  1,
152          1, -1,
153          1,  1
154     };
155 
156     IDirect3DDevice9 *device = getDevice();
157 
158     HRESULT hr = device->CreateVertexBuffer(sizeof(quad), D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &mQuadVertexBuffer, NULL);
159 
160     if (FAILED(hr))
161     {
162         ASSERT(hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
163         return error(GL_OUT_OF_MEMORY);
164     }
165 
166     void *lockPtr;
167     mQuadVertexBuffer->Lock(0, 0, &lockPtr, 0);
168     memcpy(lockPtr, quad, sizeof(quad));
169     mQuadVertexBuffer->Unlock();
170 
171     static const D3DVERTEXELEMENT9 elements[] =
172     {
173         { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
174         D3DDECL_END()
175     };
176 
177     hr = device->CreateVertexDeclaration(elements, &mQuadVertexDeclaration);
178     if (FAILED(hr))
179     {
180         ASSERT(hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
181         return error(GL_OUT_OF_MEMORY);
182     }
183 }
184 
185 template <class D3DShaderType>
setShader(ShaderId source,const char * profile,HRESULT (WINAPI IDirect3DDevice9::* createShader)(const DWORD *,D3DShaderType **),HRESULT (WINAPI IDirect3DDevice9::* setShader)(D3DShaderType *))186 bool Blit::setShader(ShaderId source, const char *profile,
187                      HRESULT (WINAPI IDirect3DDevice9::*createShader)(const DWORD *, D3DShaderType**),
188                      HRESULT (WINAPI IDirect3DDevice9::*setShader)(D3DShaderType*))
189 {
190     IDirect3DDevice9 *device = getDevice();
191 
192     D3DShaderType *shader;
193 
194     if (mCompiledShaders[source] != NULL)
195     {
196         shader = static_cast<D3DShaderType*>(mCompiledShaders[source]);
197     }
198     else
199     {
200         ID3DXBuffer *shaderCode;
201         HRESULT hr = D3DXCompileShader(mShaderSource[source], strlen(mShaderSource[source]), NULL, NULL, "main", profile, 0, &shaderCode, NULL, NULL);
202 
203         if (FAILED(hr))
204         {
205             ERR("Failed to compile %s shader for blit operation %d, error 0x%08X.", profile, (int)source, hr);
206             return false;
207         }
208 
209         hr = (device->*createShader)(static_cast<const DWORD*>(shaderCode->GetBufferPointer()), &shader);
210         if (FAILED(hr))
211         {
212             shaderCode->Release();
213             ERR("Failed to create %s shader for blit operation %d, error 0x%08X.", profile, (int)source, hr);
214             return false;
215         }
216 
217         shaderCode->Release();
218 
219         mCompiledShaders[source] = shader;
220     }
221 
222     HRESULT hr = (device->*setShader)(shader);
223 
224     if (FAILED(hr))
225     {
226         ERR("Failed to set %s shader for blit operation %d, error 0x%08X.", profile, (int)source, hr);
227         return false;
228     }
229 
230     return true;
231 }
232 
setVertexShader(ShaderId shader)233 bool Blit::setVertexShader(ShaderId shader)
234 {
235     return setShader<IDirect3DVertexShader9>(shader, mContext->supportsShaderModel3() ? "vs_3_0" : "vs_2_0", &IDirect3DDevice9::CreateVertexShader, &IDirect3DDevice9::SetVertexShader);
236 }
237 
setPixelShader(ShaderId shader)238 bool Blit::setPixelShader(ShaderId shader)
239 {
240     return setShader<IDirect3DPixelShader9>(shader, mContext->supportsShaderModel3() ? "ps_3_0" : "ps_2_0", &IDirect3DDevice9::CreatePixelShader, &IDirect3DDevice9::SetPixelShader);
241 }
242 
getSurfaceRect(IDirect3DSurface9 * surface) const243 RECT Blit::getSurfaceRect(IDirect3DSurface9 *surface) const
244 {
245     D3DSURFACE_DESC desc;
246     surface->GetDesc(&desc);
247 
248     RECT rect;
249     rect.left = 0;
250     rect.top = 0;
251     rect.right = desc.Width;
252     rect.bottom = desc.Height;
253 
254     return rect;
255 }
256 
boxFilter(IDirect3DSurface9 * source,IDirect3DSurface9 * dest)257 bool Blit::boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest)
258 {
259     IDirect3DTexture9 *texture = copySurfaceToTexture(source, getSurfaceRect(source));
260     if (!texture)
261     {
262         return false;
263     }
264 
265     IDirect3DDevice9 *device = getDevice();
266 
267     saveState();
268 
269     device->SetTexture(0, texture);
270     device->SetRenderTarget(0, dest);
271 
272     setVertexShader(SHADER_VS_STANDARD);
273     setPixelShader(SHADER_PS_PASSTHROUGH);
274 
275     setCommonBlitState();
276     device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
277     device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
278 
279     setViewport(getSurfaceRect(dest), 0, 0);
280 
281     render();
282 
283     texture->Release();
284 
285     restoreState();
286 
287     return true;
288 }
289 
formatConvert(IDirect3DSurface9 * source,const RECT & sourceRect,GLenum destFormat,GLint xoffset,GLint yoffset,IDirect3DSurface9 * dest)290 bool Blit::formatConvert(IDirect3DSurface9 *source, const RECT &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, IDirect3DSurface9 *dest)
291 {
292     IDirect3DTexture9 *texture = copySurfaceToTexture(source, sourceRect);
293     if (!texture)
294     {
295         return false;
296     }
297 
298     IDirect3DDevice9 *device = getDevice();
299 
300     saveState();
301 
302     device->SetTexture(0, texture);
303     device->SetRenderTarget(0, dest);
304 
305     setViewport(sourceRect, xoffset, yoffset);
306 
307     setCommonBlitState();
308     if (setFormatConvertShaders(destFormat))
309     {
310         render();
311     }
312 
313     texture->Release();
314 
315     restoreState();
316 
317     return true;
318 }
319 
setFormatConvertShaders(GLenum destFormat)320 bool Blit::setFormatConvertShaders(GLenum destFormat)
321 {
322     bool okay = setVertexShader(SHADER_VS_STANDARD);
323 
324     switch (destFormat)
325     {
326       default: UNREACHABLE();
327       case GL_RGBA:
328       case GL_BGRA_EXT:
329       case GL_RGB:
330       case GL_ALPHA:
331         okay = okay && setPixelShader(SHADER_PS_COMPONENTMASK);
332         break;
333 
334       case GL_LUMINANCE:
335       case GL_LUMINANCE_ALPHA:
336         okay = okay && setPixelShader(SHADER_PS_LUMINANCE);
337         break;
338     }
339 
340     if (!okay)
341     {
342         return false;
343     }
344 
345     enum { X = 0, Y = 1, Z = 2, W = 3 };
346 
347     // The meaning of this constant depends on the shader that was selected.
348     // See the shader assembly code above for details.
349     float psConst0[4] = { 0, 0, 0, 0 };
350 
351     switch (destFormat)
352     {
353       default: UNREACHABLE();
354       case GL_RGBA:
355       case GL_BGRA_EXT:
356         psConst0[X] = 1;
357         psConst0[Z] = 1;
358         break;
359 
360       case GL_RGB:
361         psConst0[X] = 1;
362         psConst0[W] = 1;
363         break;
364 
365       case GL_ALPHA:
366         psConst0[Z] = 1;
367         break;
368 
369       case GL_LUMINANCE:
370         psConst0[Y] = 1;
371         break;
372 
373       case GL_LUMINANCE_ALPHA:
374         psConst0[X] = 1;
375         break;
376     }
377 
378     getDevice()->SetPixelShaderConstantF(0, psConst0, 1);
379 
380     return true;
381 }
382 
copySurfaceToTexture(IDirect3DSurface9 * surface,const RECT & sourceRect)383 IDirect3DTexture9 *Blit::copySurfaceToTexture(IDirect3DSurface9 *surface, const RECT &sourceRect)
384 {
385     if (!surface)
386     {
387         return NULL;
388     }
389 
390     egl::Display *display = getDisplay();
391     IDirect3DDevice9 *device = getDevice();
392 
393     D3DSURFACE_DESC sourceDesc;
394     surface->GetDesc(&sourceDesc);
395 
396     // Copy the render target into a texture
397     IDirect3DTexture9 *texture;
398     HRESULT result = device->CreateTexture(sourceRect.right - sourceRect.left, sourceRect.bottom - sourceRect.top, 1, D3DUSAGE_RENDERTARGET, sourceDesc.Format, D3DPOOL_DEFAULT, &texture, NULL);
399 
400     if (FAILED(result))
401     {
402         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
403         return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
404     }
405 
406     IDirect3DSurface9 *textureSurface;
407     result = texture->GetSurfaceLevel(0, &textureSurface);
408 
409     if (FAILED(result))
410     {
411         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
412         texture->Release();
413         return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
414     }
415 
416     display->endScene();
417     result = device->StretchRect(surface, &sourceRect, textureSurface, NULL, D3DTEXF_NONE);
418 
419     textureSurface->Release();
420 
421     if (FAILED(result))
422     {
423         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
424         texture->Release();
425         return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
426     }
427 
428     return texture;
429 }
430 
setViewport(const RECT & sourceRect,GLint xoffset,GLint yoffset)431 void Blit::setViewport(const RECT &sourceRect, GLint xoffset, GLint yoffset)
432 {
433     IDirect3DDevice9 *device = getDevice();
434 
435     D3DVIEWPORT9 vp;
436     vp.X      = xoffset;
437     vp.Y      = yoffset;
438     vp.Width  = sourceRect.right - sourceRect.left;
439     vp.Height = sourceRect.bottom - sourceRect.top;
440     vp.MinZ   = 0.0f;
441     vp.MaxZ   = 1.0f;
442     device->SetViewport(&vp);
443 
444     float halfPixelAdjust[4] = { -1.0f/vp.Width, 1.0f/vp.Height, 0, 0 };
445     device->SetVertexShaderConstantF(0, halfPixelAdjust, 1);
446 }
447 
setCommonBlitState()448 void Blit::setCommonBlitState()
449 {
450     IDirect3DDevice9 *device = getDevice();
451 
452     device->SetDepthStencilSurface(NULL);
453 
454     device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
455     device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
456     device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
457     device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
458     device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
459     device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
460     device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
461     device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
462 
463     device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
464     device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
465     device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE);
466     device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
467     device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
468 
469     RECT scissorRect = {0};   // Scissoring is disabled for flipping, but we need this to capture and restore the old rectangle
470     device->SetScissorRect(&scissorRect);
471 }
472 
render()473 void Blit::render()
474 {
475     egl::Display *display = getDisplay();
476     IDirect3DDevice9 *device = getDevice();
477 
478     HRESULT hr = device->SetStreamSource(0, mQuadVertexBuffer, 0, 2 * sizeof(float));
479     hr = device->SetVertexDeclaration(mQuadVertexDeclaration);
480 
481     display->startScene();
482     hr = device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
483 }
484 
saveState()485 void Blit::saveState()
486 {
487     IDirect3DDevice9 *device = getDevice();
488 
489     HRESULT hr;
490 
491     device->GetDepthStencilSurface(&mSavedDepthStencil);
492     device->GetRenderTarget(0, &mSavedRenderTarget);
493 
494     if (mSavedStateBlock == NULL)
495     {
496         hr = device->BeginStateBlock();
497         ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
498 
499         setCommonBlitState();
500 
501         static const float dummyConst[4] = { 0, 0, 0, 0 };
502 
503         device->SetVertexShader(NULL);
504         device->SetVertexShaderConstantF(0, dummyConst, 1);
505         device->SetPixelShader(NULL);
506         device->SetPixelShaderConstantF(0, dummyConst, 1);
507 
508         D3DVIEWPORT9 dummyVp;
509         dummyVp.X = 0;
510         dummyVp.Y = 0;
511         dummyVp.Width = 1;
512         dummyVp.Height = 1;
513         dummyVp.MinZ = 0;
514         dummyVp.MaxZ = 1;
515 
516         device->SetViewport(&dummyVp);
517 
518         device->SetTexture(0, NULL);
519 
520         device->SetStreamSource(0, mQuadVertexBuffer, 0, 0);
521 
522         device->SetVertexDeclaration(mQuadVertexDeclaration);
523 
524         hr = device->EndStateBlock(&mSavedStateBlock);
525         ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
526     }
527 
528     ASSERT(mSavedStateBlock != NULL);
529 
530     if (mSavedStateBlock != NULL)
531     {
532         hr = mSavedStateBlock->Capture();
533         ASSERT(SUCCEEDED(hr));
534     }
535 }
536 
restoreState()537 void Blit::restoreState()
538 {
539     IDirect3DDevice9 *device = getDevice();
540 
541     device->SetDepthStencilSurface(mSavedDepthStencil);
542     if (mSavedDepthStencil != NULL)
543     {
544         mSavedDepthStencil->Release();
545         mSavedDepthStencil = NULL;
546     }
547 
548     device->SetRenderTarget(0, mSavedRenderTarget);
549     if (mSavedRenderTarget != NULL)
550     {
551         mSavedRenderTarget->Release();
552         mSavedRenderTarget = NULL;
553     }
554 
555     ASSERT(mSavedStateBlock != NULL);
556 
557     if (mSavedStateBlock != NULL)
558     {
559         mSavedStateBlock->Apply();
560     }
561 }
562 
563 }
564