Home | History | Annotate | Download | only in libGLESv2
      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 
    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 
    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 
    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>
    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 
    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 
    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 
    243 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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