blob: 136cb3566e9501db986ee64745edd97721ff3c40 [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
9#include "Blit.h"
10
11#include <d3dx9.h>
12
13#include "main.h"
14#include "common/debug.h"
15
16namespace
17{
18// Standard Vertex Shader
19// Input 0 is the homogenous position.
20// Outputs the homogenous position as-is.
21// Outputs a tex coord with (0,0) in the upper-left corner of the screen and (1,1) in the bottom right.
22// C0.X must be negative half-pixel width, C0.Y must be half-pixel height. C0.ZW must be 0.
23const char standardvs[] =
24"struct VS_OUTPUT\n"
25"{\n"
26" float4 position : POSITION;\n"
27" float4 texcoord : TEXCOORD0;\n"
28"};\n"
29"\n"
30"uniform float4 halfPixelSize : c0;\n"
31"\n"
32"VS_OUTPUT main(in float4 position : POSITION)\n"
33"{\n"
34" VS_OUTPUT Out;\n"
35"\n"
36" Out.position = position + halfPixelSize;\n"
37" Out.texcoord = position * float4(0.5, -0.5, 1.0, 1.0) + float4(0.5, 0.5, 0, 0);\n"
38"\n"
39" return Out;\n"
40"}\n";
41
42// Flip Y Vertex Shader
43// Input 0 is the homogenous position.
44// Outputs the homogenous position as-is.
45// Outputs a tex coord with (0,1) in the upper-left corner of the screen and (1,0) in the bottom right.
46// C0.XY must be the half-pixel width and height. C0.ZW must be 0.
47const char flipyvs[] =
48"struct VS_OUTPUT\n"
49"{\n"
50" float4 position : POSITION;\n"
51" float4 texcoord : TEXCOORD0;\n"
52"};\n"
53"\n"
54"uniform float4 halfPixelSize : c0;\n"
55"\n"
56"VS_OUTPUT main(in float4 position : POSITION)\n"
57"{\n"
58" VS_OUTPUT Out;\n"
59"\n"
60" Out.position = position + halfPixelSize;\n"
61" Out.texcoord = position * float4(0.5, 0.5, 1.0, 1.0) + float4(0.5, 0.5, 0, 0);\n"
62"\n"
63" return Out;\n"
64"}\n";
65
66// Passthrough Pixel Shader
67// Outputs texture 0 sampled at texcoord 0.
68const char passthroughps[] =
69"sampler2D tex : s0;\n"
70"\n"
71"float4 main(float4 texcoord : TEXCOORD0) : COLOR\n"
72"{\n"
73" return tex2D(tex, texcoord.xy);\n"
74"}\n";
75
76// Luminance Conversion Pixel Shader
77// Outputs sample(tex0, tc0).rrra.
78// For LA output (pass A) set C0.X = 1, C0.Y = 0.
79// For L output (A = 1) set C0.X = 0, C0.Y = 1.
80const char luminanceps[] =
81"sampler2D tex : s0;\n"
82"\n"
83"uniform float4 mode : c0;\n"
84"\n"
85"float4 main(float4 texcoord : TEXCOORD0) : COLOR\n"
86"{\n"
87" float4 tmp = tex2D(tex, texcoord.xy);\n"
88" tmp.w = tmp.w * mode.x + mode.y;\n"
89" return tmp.xxxw;\n"
90"}\n";
91
92// RGB/A Component Mask Pixel Shader
93// Outputs sample(tex0, tc0) with options to force RGB = 0 and/or A = 1.
94// To force RGB = 0, set C0.X = 0, otherwise C0.X = 1.
95// To force A = 1, set C0.Z = 0, C0.W = 1, otherwise C0.Z = 1, C0.W = 0.
96const char componentmaskps[] =
97"sampler2D tex : s0;\n"
98"\n"
99"uniform float4 mode : c0;\n"
100"\n"
101"float4 main(float4 texcoord : TEXCOORD0) : COLOR\n"
102"{\n"
103" float4 tmp = tex2D(tex, texcoord.xy);\n"
104" tmp.xyz = tmp.xyz * mode.x;\n"
105" tmp.w = tmp.w * mode.z + mode.w;\n"
106" return tmp;\n"
107"}\n";
108
109}
110
111namespace gl
112{
113
114const char * const Blit::mShaderSource[] =
115{
116 standardvs,
117 flipyvs,
118 passthroughps,
119 luminanceps,
120 componentmaskps
121};
122
123Blit::Blit(Context *context)
124 : mContext(context), mQuadVertexBuffer(NULL), mQuadVertexDeclaration(NULL)
125{
126 initGeometry();
127 memset(mCompiledShaders, 0, sizeof(mCompiledShaders));
128}
129
130Blit::~Blit()
131{
132 if (mQuadVertexBuffer) mQuadVertexBuffer->Release();
133 if (mQuadVertexDeclaration) mQuadVertexDeclaration->Release();
134
135 for (int i = 0; i < SHADER_COUNT; i++)
136 {
137 if (mCompiledShaders[i])
138 {
139 mCompiledShaders[i]->Release();
140 }
141 }
142}
143
144void Blit::initGeometry()
145{
146 static const float quad[] =
147 {
148 -1, -1,
149 -1, 1,
150 1, -1,
151 1, 1
152 };
153
154 IDirect3DDevice9 *device = getDevice();
155
156 HRESULT hr = device->CreateVertexBuffer(sizeof(quad), D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &mQuadVertexBuffer, NULL);
157
158 if (FAILED(hr))
159 {
160 ASSERT(hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
161 return error(GL_OUT_OF_MEMORY);
162 }
163
164 void *lockPtr;
165 mQuadVertexBuffer->Lock(0, 0, &lockPtr, 0);
166 memcpy(lockPtr, quad, sizeof(quad));
167 mQuadVertexBuffer->Unlock();
168
169 static const D3DVERTEXELEMENT9 elements[] =
170 {
171 { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
172 D3DDECL_END()
173 };
174
175 hr = device->CreateVertexDeclaration(elements, &mQuadVertexDeclaration);
176 if (FAILED(hr))
177 {
178 ASSERT(hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
179 return error(GL_OUT_OF_MEMORY);
180 }
181}
182
183template <class D3DShaderType>
184bool Blit::setShader(ShaderId source, const char *profile,
185 HRESULT (WINAPI IDirect3DDevice9::*createShader)(const DWORD *, D3DShaderType**),
186 HRESULT (WINAPI IDirect3DDevice9::*setShader)(D3DShaderType*))
187{
188 IDirect3DDevice9 *device = getDevice();
189
190 D3DShaderType *shader;
191
192 if (mCompiledShaders[source] != NULL)
193 {
194 shader = static_cast<D3DShaderType*>(mCompiledShaders[source]);
195 }
196 else
197 {
198 ID3DXBuffer *shaderCode;
199 HRESULT hr = D3DXCompileShader(mShaderSource[source], strlen(mShaderSource[source]), NULL, NULL, "main", profile, 0, &shaderCode, NULL, NULL);
200
201 if (FAILED(hr))
202 {
203 ERR("Failed to compile %s shader for blit operation %d, error 0x%08X.", profile, (int)source, hr);
204 return false;
205 }
206
207 hr = (device->*createShader)(static_cast<const DWORD*>(shaderCode->GetBufferPointer()), &shader);
208 if (FAILED(hr))
209 {
210 shaderCode->Release();
211 ERR("Failed to create %s shader for blit operation %d, error 0x%08X.", profile, (int)source, hr);
212 return false;
213 }
214
215 shaderCode->Release();
216
217 mCompiledShaders[source] = shader;
218 }
219
220 HRESULT hr = (device->*setShader)(shader);
221
222 if (FAILED(hr))
223 {
224 ERR("Failed to set %s shader for blit operation %d, error 0x%08X.", profile, (int)source, hr);
225 return false;
226 }
227
228 return true;
229}
230
231bool Blit::setVertexShader(ShaderId shader)
232{
233 return setShader<IDirect3DVertexShader9>(shader, mContext->getVertexShaderProfile(), &IDirect3DDevice9::CreateVertexShader, &IDirect3DDevice9::SetVertexShader);
234}
235
236bool Blit::setPixelShader(ShaderId shader)
237{
238 return setShader<IDirect3DPixelShader9>(shader, mContext->getPixelShaderProfile(), &IDirect3DDevice9::CreatePixelShader, &IDirect3DDevice9::SetPixelShader);
239}
240
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000241RECT Blit::getSurfaceRect(IDirect3DSurface9 *surface) const
242{
243 D3DSURFACE_DESC desc;
244 surface->GetDesc(&desc);
245
246 RECT rect;
247 rect.left = 0;
248 rect.top = 0;
249 rect.right = desc.Width;
250 rect.bottom = desc.Height;
251
252 return rect;
253}
254
255bool Blit::boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest)
256{
257 IDirect3DTexture9 *texture = copySurfaceToTexture(source, getSurfaceRect(source));
258 if (!texture)
259 {
260 return false;
261 }
262
263 IDirect3DDevice9 *device = getDevice();
264 device->SetTexture(0, texture);
265 device->SetRenderTarget(0, dest);
266
267 setVertexShader(SHADER_VS_STANDARD);
268 setPixelShader(SHADER_PS_PASSTHROUGH);
269
270 setCommonBlitState();
271 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
272 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
273
274 setViewport(getSurfaceRect(dest), 0, 0);
275
276 render();
277
278 texture->Release();
279
280 return true;
281}
282
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000283bool Blit::formatConvert(IDirect3DSurface9 *source, const RECT &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, IDirect3DSurface9 *dest)
284{
285 IDirect3DTexture9 *texture = copySurfaceToTexture(source, sourceRect);
286 if (!texture)
287 {
288 return false;
289 }
290
291 IDirect3DDevice9 *device = getDevice();
292
293 device->SetTexture(0, texture);
294 device->SetRenderTarget(0, dest);
295
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000296 setViewport(sourceRect, xoffset, yoffset);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000297
298 setCommonBlitState();
299 if (setFormatConvertShaders(destFormat))
300 {
301 render();
302 }
303
304 texture->Release();
305
306 return true;
307}
308
309bool Blit::setFormatConvertShaders(GLenum destFormat)
310{
311 bool okay = setVertexShader(SHADER_VS_STANDARD);
312
313 switch (destFormat)
314 {
315 default: UNREACHABLE();
316 case GL_RGBA:
317 case GL_RGB:
318 case GL_ALPHA:
319 okay = okay && setPixelShader(SHADER_PS_COMPONENTMASK);
320 break;
321
322 case GL_LUMINANCE:
323 case GL_LUMINANCE_ALPHA:
324 okay = okay && setPixelShader(SHADER_PS_LUMINANCE);
325 break;
326 }
327
328 if (!okay)
329 {
330 return false;
331 }
332
333 enum { X = 0, Y = 1, Z = 2, W = 3 };
334
335 // The meaning of this constant depends on the shader that was selected.
336 // See the shader assembly code above for details.
337 float psConst0[4] = { 0, 0, 0, 0 };
338
339 switch (destFormat)
340 {
341 default: UNREACHABLE();
342 case GL_RGBA:
343 psConst0[X] = 1;
344 psConst0[Z] = 1;
345 break;
346
347 case GL_RGB:
348 psConst0[X] = 1;
349 psConst0[W] = 1;
350 break;
351
352 case GL_ALPHA:
353 psConst0[Z] = 1;
354 break;
355
356 case GL_LUMINANCE:
357 psConst0[Y] = 1;
358 break;
359
360 case GL_LUMINANCE_ALPHA:
361 psConst0[X] = 1;
362 break;
363 }
364
365 getDevice()->SetPixelShaderConstantF(0, psConst0, 1);
366
367 return true;
368}
369
370IDirect3DTexture9 *Blit::copySurfaceToTexture(IDirect3DSurface9 *surface, const RECT &sourceRect)
371{
372 IDirect3DDevice9 *device = getDevice();
373
374 D3DSURFACE_DESC sourceDesc;
375 surface->GetDesc(&sourceDesc);
376
377 // Copy the render target into a texture
378 IDirect3DTexture9 *texture;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000379 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 +0000380
381 if (FAILED(result))
382 {
383 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
384 return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
385 }
386
387 IDirect3DSurface9 *textureSurface;
388 result = texture->GetSurfaceLevel(0, &textureSurface);
389
390 if (FAILED(result))
391 {
392 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
393 texture->Release();
394 return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
395 }
396
397 RECT d3dSourceRect;
398 d3dSourceRect.left = sourceRect.left;
399 d3dSourceRect.right = sourceRect.right;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000400 d3dSourceRect.top = sourceRect.top;
401 d3dSourceRect.bottom = sourceRect.bottom;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000402
403 result = device->StretchRect(surface, &d3dSourceRect, textureSurface, NULL, D3DTEXF_NONE);
404
405 textureSurface->Release();
406
407 if (FAILED(result))
408 {
409 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
410 texture->Release();
411 return error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
412 }
413
414 return texture;
415}
416
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000417void Blit::setViewport(const RECT &sourceRect, GLint xoffset, GLint yoffset)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000418{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000419 IDirect3DDevice9 *device = getDevice();
420
421 D3DVIEWPORT9 vp;
422 vp.X = xoffset;
423 vp.Y = yoffset;
424 vp.Width = sourceRect.right - sourceRect.left;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000425 vp.Height = sourceRect.bottom - sourceRect.top;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000426 vp.MinZ = 0.0f;
427 vp.MaxZ = 1.0f;
428 device->SetViewport(&vp);
429
430 float halfPixelAdjust[4] = { -1.0f/vp.Width, 1.0f/vp.Height, 0, 0 };
431 device->SetVertexShaderConstantF(0, halfPixelAdjust, 1);
432}
433
434void Blit::setCommonBlitState()
435{
436 IDirect3DDevice9 *device = getDevice();
437
438 device->SetDepthStencilSurface(NULL);
439
440 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
441 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
442 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
443 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
444 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
445 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
446 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
447 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
448
449 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
450 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
451 device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE);
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000452 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
453 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000454}
455
456void Blit::render()
457{
458 IDirect3DDevice9 *device = getDevice();
459
460 HRESULT hr = device->SetStreamSource(0, mQuadVertexBuffer, 0, 2 * sizeof(float));
461 hr = device->SetVertexDeclaration(mQuadVertexDeclaration);
462
463 device->BeginScene();
464 hr = device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
465 device->EndScene();
466}
467
468}