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