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