blob: 48b40341bea343458c3902723256128e0c2b1908 [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)
daniel@transgaming.com1ddd1dd2010-05-11 02:29:34 +0000125 : mContext(context), mQuadVertexBuffer(NULL), mQuadVertexDeclaration(NULL), mSavedRenderTarget(NULL), mSavedDepthStencil(NULL), mSavedStateBlock(NULL)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000126{
127 initGeometry();
128 memset(mCompiledShaders, 0, sizeof(mCompiledShaders));
129}
130
131Blit::~Blit()
132{
daniel@transgaming.com1ddd1dd2010-05-11 02:29:34 +0000133 if (mSavedStateBlock) mSavedStateBlock->Release();
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000134 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{
daniel@transgaming.combe5a0862010-07-28 19:20:37 +0000235 return setShader<IDirect3DVertexShader9>(shader, mContext->supportsShaderModel3() ? "vs_3_0" : "vs_2_0", &IDirect3DDevice9::CreateVertexShader, &IDirect3DDevice9::SetVertexShader);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000236}
237
238bool Blit::setPixelShader(ShaderId shader)
239{
daniel@transgaming.combe5a0862010-07-28 19:20:37 +0000240 return setShader<IDirect3DPixelShader9>(shader, mContext->supportsShaderModel3() ? "ps_3_0" : "ps_2_0", &IDirect3DDevice9::CreatePixelShader, &IDirect3DDevice9::SetPixelShader);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000241}
242
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000243RECT 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();
daniel@transgaming.com1ddd1dd2010-05-11 02:29:34 +0000266
267 saveState();
268
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000269 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
daniel@transgaming.com1ddd1dd2010-05-11 02:29:34 +0000285 restoreState();
286
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000287 return true;
288}
289
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000290bool 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
daniel@transgaming.com1ddd1dd2010-05-11 02:29:34 +0000300 saveState();
301
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000302 device->SetTexture(0, texture);
303 device->SetRenderTarget(0, dest);
304
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000305 setViewport(sourceRect, xoffset, yoffset);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000306
307 setCommonBlitState();
308 if (setFormatConvertShaders(destFormat))
309 {
310 render();
311 }
312
313 texture->Release();
314
daniel@transgaming.com1ddd1dd2010-05-11 02:29:34 +0000315 restoreState();
316
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000317 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_RGB:
329 case GL_ALPHA:
330 okay = okay && setPixelShader(SHADER_PS_COMPONENTMASK);
331 break;
332
333 case GL_LUMINANCE:
334 case GL_LUMINANCE_ALPHA:
335 okay = okay && setPixelShader(SHADER_PS_LUMINANCE);
336 break;
337 }
338
339 if (!okay)
340 {
341 return false;
342 }
343
344 enum { X = 0, Y = 1, Z = 2, W = 3 };
345
346 // The meaning of this constant depends on the shader that was selected.
347 // See the shader assembly code above for details.
348 float psConst0[4] = { 0, 0, 0, 0 };
349
350 switch (destFormat)
351 {
352 default: UNREACHABLE();
353 case GL_RGBA:
354 psConst0[X] = 1;
355 psConst0[Z] = 1;
356 break;
357
358 case GL_RGB:
359 psConst0[X] = 1;
360 psConst0[W] = 1;
361 break;
362
363 case GL_ALPHA:
364 psConst0[Z] = 1;
365 break;
366
367 case GL_LUMINANCE:
368 psConst0[Y] = 1;
369 break;
370
371 case GL_LUMINANCE_ALPHA:
372 psConst0[X] = 1;
373 break;
374 }
375
376 getDevice()->SetPixelShaderConstantF(0, psConst0, 1);
377
378 return true;
379}
380
381IDirect3DTexture9 *Blit::copySurfaceToTexture(IDirect3DSurface9 *surface, const RECT &sourceRect)
382{
daniel@transgaming.comae072af2010-05-05 18:47:28 +0000383 egl::Display *display = getDisplay();
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000384 IDirect3DDevice9 *device = getDevice();
385
386 D3DSURFACE_DESC sourceDesc;
387 surface->GetDesc(&sourceDesc);
388
389 // Copy the render target into a texture
390 IDirect3DTexture9 *texture;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000391 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 +0000392
393 if (FAILED(result))
394 {
395 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
396 return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
397 }
398
399 IDirect3DSurface9 *textureSurface;
400 result = texture->GetSurfaceLevel(0, &textureSurface);
401
402 if (FAILED(result))
403 {
404 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
405 texture->Release();
406 return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
407 }
408
409 RECT d3dSourceRect;
410 d3dSourceRect.left = sourceRect.left;
411 d3dSourceRect.right = sourceRect.right;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000412 d3dSourceRect.top = sourceRect.top;
413 d3dSourceRect.bottom = sourceRect.bottom;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000414
daniel@transgaming.comae072af2010-05-05 18:47:28 +0000415 display->endScene();
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000416 result = device->StretchRect(surface, &d3dSourceRect, textureSurface, NULL, D3DTEXF_NONE);
417
418 textureSurface->Release();
419
420 if (FAILED(result))
421 {
422 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
423 texture->Release();
424 return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
425 }
426
427 return texture;
428}
429
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000430void Blit::setViewport(const RECT &sourceRect, GLint xoffset, GLint yoffset)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000431{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000432 IDirect3DDevice9 *device = getDevice();
433
434 D3DVIEWPORT9 vp;
435 vp.X = xoffset;
436 vp.Y = yoffset;
437 vp.Width = sourceRect.right - sourceRect.left;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000438 vp.Height = sourceRect.bottom - sourceRect.top;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000439 vp.MinZ = 0.0f;
440 vp.MaxZ = 1.0f;
441 device->SetViewport(&vp);
442
443 float halfPixelAdjust[4] = { -1.0f/vp.Width, 1.0f/vp.Height, 0, 0 };
444 device->SetVertexShaderConstantF(0, halfPixelAdjust, 1);
445}
446
447void Blit::setCommonBlitState()
448{
449 IDirect3DDevice9 *device = getDevice();
450
451 device->SetDepthStencilSurface(NULL);
452
453 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
454 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
455 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
456 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
457 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
458 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
459 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
460 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
461
462 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
463 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
464 device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE);
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000465 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
466 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
daniel@transgaming.com81655a72010-05-20 19:18:17 +0000467
468 for (int i = 0; i < MAX_VERTEX_ATTRIBS+1; i++)
469 {
470 device->SetStreamSourceFreq(i, 1);
471 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000472}
473
474void Blit::render()
475{
daniel@transgaming.comae072af2010-05-05 18:47:28 +0000476 egl::Display *display = getDisplay();
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000477 IDirect3DDevice9 *device = getDevice();
478
479 HRESULT hr = device->SetStreamSource(0, mQuadVertexBuffer, 0, 2 * sizeof(float));
480 hr = device->SetVertexDeclaration(mQuadVertexDeclaration);
481
daniel@transgaming.comae072af2010-05-05 18:47:28 +0000482 display->startScene();
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000483 hr = device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000484}
485
daniel@transgaming.com1ddd1dd2010-05-11 02:29:34 +0000486void Blit::saveState()
487{
488 IDirect3DDevice9 *device = getDevice();
489
490 HRESULT hr;
491
492 device->GetDepthStencilSurface(&mSavedDepthStencil);
493 device->GetRenderTarget(0, &mSavedRenderTarget);
494
495 if (mSavedStateBlock == NULL)
496 {
497 hr = device->BeginStateBlock();
498 ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
499
500 setCommonBlitState();
501
502 static const float dummyConst[4] = { 0, 0, 0, 0 };
503
504 device->SetVertexShader(NULL);
505 device->SetVertexShaderConstantF(0, dummyConst, 1);
506 device->SetPixelShader(NULL);
507 device->SetPixelShaderConstantF(0, dummyConst, 1);
508
509 D3DVIEWPORT9 dummyVp;
510 dummyVp.X = 0;
511 dummyVp.Y = 0;
512 dummyVp.Width = 1;
513 dummyVp.Height = 1;
514 dummyVp.MinZ = 0;
515 dummyVp.MaxZ = 1;
516
517 device->SetViewport(&dummyVp);
518
519 device->SetTexture(0, NULL);
520
521 device->SetStreamSource(0, mQuadVertexBuffer, 0, 0);
522
523 device->SetVertexDeclaration(mQuadVertexDeclaration);
524
525 hr = device->EndStateBlock(&mSavedStateBlock);
526 ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
527 }
528
529 ASSERT(mSavedStateBlock != NULL);
530
531 if (mSavedStateBlock != NULL)
532 {
533 hr = mSavedStateBlock->Capture();
534 ASSERT(SUCCEEDED(hr));
535 }
536}
537
538void Blit::restoreState()
539{
540 IDirect3DDevice9 *device = getDevice();
541
542 device->SetDepthStencilSurface(mSavedDepthStencil);
543 if (mSavedDepthStencil != NULL)
544 {
545 mSavedDepthStencil->Release();
546 mSavedDepthStencil = NULL;
547 }
548
549 device->SetRenderTarget(0, mSavedRenderTarget);
550 if (mSavedRenderTarget != NULL)
551 {
552 mSavedRenderTarget->Release();
553 mSavedRenderTarget = NULL;
554 }
555
556 ASSERT(mSavedStateBlock != NULL);
557
558 if (mSavedStateBlock != NULL)
559 {
560 mSavedStateBlock->Apply();
561 }
562}
563
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000564}