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 17namespace 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), mSavedRenderTarget(NULL), mSavedDepthStencil(NULL), mSavedStateBlock(NULL) 126{ 127 initGeometry(); 128 memset(mCompiledShaders, 0, sizeof(mCompiledShaders)); 129} 130 131Blit::~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 146void 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 185template <class D3DShaderType> 186bool 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 233bool Blit::setVertexShader(ShaderId shader) 234{ 235 return setShader<IDirect3DVertexShader9>(shader, mContext->supportsShaderModel3() ? "vs_3_0" : "vs_2_0", &IDirect3DDevice9::CreateVertexShader, &IDirect3DDevice9::SetVertexShader); 236} 237 238bool Blit::setPixelShader(ShaderId shader) 239{ 240 return setShader<IDirect3DPixelShader9>(shader, mContext->supportsShaderModel3() ? "ps_3_0" : "ps_2_0", &IDirect3DDevice9::CreatePixelShader, &IDirect3DDevice9::SetPixelShader); 241} 242 243RECT 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 257bool 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 290bool 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 320bool 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 383IDirect3DTexture9 *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 431void 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 448void 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 473void 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 485void 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 537void 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