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// Blit9.cpp: Surface copy utility class.
8
9#include "libGLESv2/renderer/d3d/d3d9/Blit9.h"
10#include "libGLESv2/renderer/d3d/d3d9/renderer9_utils.h"
11#include "libGLESv2/renderer/d3d/d3d9/formatutils9.h"
12#include "libGLESv2/renderer/d3d/d3d9/TextureStorage9.h"
13#include "libGLESv2/renderer/d3d/d3d9/RenderTarget9.h"
14#include "libGLESv2/renderer/d3d/d3d9/Renderer9.h"
15#include "libGLESv2/Framebuffer.h"
16#include "libGLESv2/FramebufferAttachment.h"
17#include "libGLESv2/main.h"
18
19namespace
20{
21// Precompiled shaders
22#include "libGLESv2/renderer/d3d/d3d9/shaders/compiled/standardvs.h"
23#include "libGLESv2/renderer/d3d/d3d9/shaders/compiled/flipyvs.h"
24#include "libGLESv2/renderer/d3d/d3d9/shaders/compiled/passthroughps.h"
25#include "libGLESv2/renderer/d3d/d3d9/shaders/compiled/luminanceps.h"
26#include "libGLESv2/renderer/d3d/d3d9/shaders/compiled/componentmaskps.h"
27
28const BYTE* const g_shaderCode[] =
29{
30    g_vs20_standardvs,
31    g_vs20_flipyvs,
32    g_ps20_passthroughps,
33    g_ps20_luminanceps,
34    g_ps20_componentmaskps
35};
36
37const size_t g_shaderSize[] =
38{
39    sizeof(g_vs20_standardvs),
40    sizeof(g_vs20_flipyvs),
41    sizeof(g_ps20_passthroughps),
42    sizeof(g_ps20_luminanceps),
43    sizeof(g_ps20_componentmaskps)
44};
45}
46
47namespace rx
48{
49Blit9::Blit9(rx::Renderer9 *renderer)
50  : mRenderer(renderer), mQuadVertexBuffer(NULL), mQuadVertexDeclaration(NULL), mSavedStateBlock(NULL), mSavedRenderTarget(NULL), mSavedDepthStencil(NULL)
51{
52    initGeometry();
53    memset(mCompiledShaders, 0, sizeof(mCompiledShaders));
54}
55
56Blit9::~Blit9()
57{
58    SafeRelease(mSavedStateBlock);
59    SafeRelease(mQuadVertexBuffer);
60    SafeRelease(mQuadVertexDeclaration);
61
62    for (int i = 0; i < SHADER_COUNT; i++)
63    {
64        SafeRelease(mCompiledShaders[i]);
65    }
66}
67
68void Blit9::initGeometry()
69{
70    static const float quad[] =
71    {
72        -1, -1,
73        -1,  1,
74         1, -1,
75         1,  1
76    };
77
78    IDirect3DDevice9 *device = mRenderer->getDevice();
79
80    HRESULT result = device->CreateVertexBuffer(sizeof(quad), D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &mQuadVertexBuffer, NULL);
81
82    if (FAILED(result))
83    {
84        ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
85        return gl::error(GL_OUT_OF_MEMORY);
86    }
87
88    void *lockPtr = NULL;
89    result = mQuadVertexBuffer->Lock(0, 0, &lockPtr, 0);
90
91    if (FAILED(result) || lockPtr == NULL)
92    {
93        ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
94        return gl::error(GL_OUT_OF_MEMORY);
95    }
96
97    memcpy(lockPtr, quad, sizeof(quad));
98    mQuadVertexBuffer->Unlock();
99
100    static const D3DVERTEXELEMENT9 elements[] =
101    {
102        { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
103        D3DDECL_END()
104    };
105
106    result = device->CreateVertexDeclaration(elements, &mQuadVertexDeclaration);
107
108    if (FAILED(result))
109    {
110        ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
111        return gl::error(GL_OUT_OF_MEMORY);
112    }
113}
114
115template <class D3DShaderType>
116bool Blit9::setShader(ShaderId source, const char *profile,
117                     D3DShaderType *(rx::Renderer9::*createShader)(const DWORD *, size_t length),
118                     HRESULT (WINAPI IDirect3DDevice9::*setShader)(D3DShaderType*))
119{
120    IDirect3DDevice9 *device = mRenderer->getDevice();
121
122    D3DShaderType *shader;
123
124    if (mCompiledShaders[source] != NULL)
125    {
126        shader = static_cast<D3DShaderType*>(mCompiledShaders[source]);
127    }
128    else
129    {
130        const BYTE* shaderCode = g_shaderCode[source];
131        size_t shaderSize = g_shaderSize[source];
132
133        shader = (mRenderer->*createShader)(reinterpret_cast<const DWORD*>(shaderCode), shaderSize);
134        if (!shader)
135        {
136            ERR("Failed to create shader for blit operation");
137            return false;
138        }
139
140        mCompiledShaders[source] = shader;
141    }
142
143    HRESULT hr = (device->*setShader)(shader);
144
145    if (FAILED(hr))
146    {
147        ERR("Failed to set shader for blit operation");
148        return false;
149    }
150
151    return true;
152}
153
154bool Blit9::setVertexShader(ShaderId shader)
155{
156    return setShader<IDirect3DVertexShader9>(shader, "vs_2_0", &rx::Renderer9::createVertexShader, &IDirect3DDevice9::SetVertexShader);
157}
158
159bool Blit9::setPixelShader(ShaderId shader)
160{
161    return setShader<IDirect3DPixelShader9>(shader, "ps_2_0", &rx::Renderer9::createPixelShader, &IDirect3DDevice9::SetPixelShader);
162}
163
164RECT Blit9::getSurfaceRect(IDirect3DSurface9 *surface) const
165{
166    D3DSURFACE_DESC desc;
167    surface->GetDesc(&desc);
168
169    RECT rect;
170    rect.left = 0;
171    rect.top = 0;
172    rect.right = desc.Width;
173    rect.bottom = desc.Height;
174
175    return rect;
176}
177
178bool Blit9::boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest)
179{
180    IDirect3DTexture9 *texture = copySurfaceToTexture(source, getSurfaceRect(source));
181    if (!texture)
182    {
183        return false;
184    }
185
186    IDirect3DDevice9 *device = mRenderer->getDevice();
187
188    saveState();
189
190    device->SetTexture(0, texture);
191    device->SetRenderTarget(0, dest);
192
193    setVertexShader(SHADER_VS_STANDARD);
194    setPixelShader(SHADER_PS_PASSTHROUGH);
195
196    setCommonBlitState();
197    device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
198    device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
199
200    setViewport(getSurfaceRect(dest), 0, 0);
201
202    render();
203
204    SafeRelease(texture);
205
206    restoreState();
207
208    return true;
209}
210
211bool Blit9::copy2D(gl::Framebuffer *framebuffer, const RECT &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, TextureStorage *storage, GLint level)
212{
213    RenderTarget9 *renderTarget = NULL;
214    IDirect3DSurface9 *source = NULL;
215    gl::FramebufferAttachment *colorbuffer = framebuffer->getColorbuffer(0);
216
217    if (colorbuffer)
218    {
219        renderTarget = d3d9::GetAttachmentRenderTarget(colorbuffer);
220    }
221
222    if (renderTarget)
223    {
224        source = renderTarget->getSurface();
225    }
226
227    if (!source)
228    {
229        ERR("Failed to retrieve the render target.");
230        return gl::error(GL_OUT_OF_MEMORY, false);
231    }
232
233    TextureStorage9_2D *storage9 = TextureStorage9_2D::makeTextureStorage9_2D(storage);
234    IDirect3DSurface9 *destSurface = storage9->getSurfaceLevel(level, true);
235    bool result = false;
236
237    if (destSurface)
238    {
239        result = copy(source, sourceRect, destFormat, xoffset, yoffset, destSurface);
240        SafeRelease(destSurface);
241    }
242
243    SafeRelease(source);
244    return result;
245}
246
247bool Blit9::copyCube(gl::Framebuffer *framebuffer, const RECT &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, TextureStorage *storage, GLenum target, GLint level)
248{
249    RenderTarget9 *renderTarget = NULL;
250    IDirect3DSurface9 *source = NULL;
251    gl::FramebufferAttachment *colorbuffer = framebuffer->getColorbuffer(0);
252
253    if (colorbuffer)
254    {
255        renderTarget = d3d9::GetAttachmentRenderTarget(colorbuffer);
256    }
257
258    if (renderTarget)
259    {
260        source = renderTarget->getSurface();
261    }
262
263    if (!source)
264    {
265        ERR("Failed to retrieve the render target.");
266        return gl::error(GL_OUT_OF_MEMORY, false);
267    }
268
269    TextureStorage9_Cube *storage9 = TextureStorage9_Cube::makeTextureStorage9_Cube(storage);
270    IDirect3DSurface9 *destSurface = storage9->getCubeMapSurface(target, level, true);
271    bool result = false;
272
273    if (destSurface)
274    {
275        result = copy(source, sourceRect, destFormat, xoffset, yoffset, destSurface);
276        SafeRelease(destSurface);
277    }
278
279    SafeRelease(source);
280    return result;
281}
282
283bool Blit9::copy(IDirect3DSurface9 *source, const RECT &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, IDirect3DSurface9 *dest)
284{
285    if (!dest)
286    {
287        return false;
288    }
289
290    IDirect3DDevice9 *device = mRenderer->getDevice();
291
292    D3DSURFACE_DESC sourceDesc;
293    D3DSURFACE_DESC destDesc;
294    source->GetDesc(&sourceDesc);
295    dest->GetDesc(&destDesc);
296
297    if (sourceDesc.Format == destDesc.Format && destDesc.Usage & D3DUSAGE_RENDERTARGET &&
298        d3d9_gl::IsFormatChannelEquivalent(destDesc.Format, destFormat))   // Can use StretchRect
299    {
300        RECT destRect = {xoffset, yoffset, xoffset + (sourceRect.right - sourceRect.left), yoffset + (sourceRect.bottom - sourceRect.top)};
301        HRESULT result = device->StretchRect(source, &sourceRect, dest, &destRect, D3DTEXF_POINT);
302
303        if (FAILED(result))
304        {
305            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
306            return gl::error(GL_OUT_OF_MEMORY, false);
307        }
308    }
309    else
310    {
311        return formatConvert(source, sourceRect, destFormat, xoffset, yoffset, dest);
312    }
313    return true;
314}
315
316bool Blit9::formatConvert(IDirect3DSurface9 *source, const RECT &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, IDirect3DSurface9 *dest)
317{
318    IDirect3DTexture9 *texture = copySurfaceToTexture(source, sourceRect);
319    if (!texture)
320    {
321        return false;
322    }
323
324    IDirect3DDevice9 *device = mRenderer->getDevice();
325
326    saveState();
327
328    device->SetTexture(0, texture);
329    device->SetRenderTarget(0, dest);
330
331    setViewport(sourceRect, xoffset, yoffset);
332
333    setCommonBlitState();
334    if (setFormatConvertShaders(destFormat))
335    {
336        render();
337    }
338
339    SafeRelease(texture);
340
341    restoreState();
342
343    return true;
344}
345
346bool Blit9::setFormatConvertShaders(GLenum destFormat)
347{
348    bool okay = setVertexShader(SHADER_VS_STANDARD);
349
350    switch (destFormat)
351    {
352      default: UNREACHABLE();
353      case GL_RGBA:
354      case GL_BGRA_EXT:
355      case GL_RGB:
356      case GL_RG_EXT:
357      case GL_RED_EXT:
358      case GL_ALPHA:
359        okay = okay && setPixelShader(SHADER_PS_COMPONENTMASK);
360        break;
361
362      case GL_LUMINANCE:
363      case GL_LUMINANCE_ALPHA:
364        okay = okay && setPixelShader(SHADER_PS_LUMINANCE);
365        break;
366    }
367
368    if (!okay)
369    {
370        return false;
371    }
372
373    enum { X = 0, Y = 1, Z = 2, W = 3 };
374
375    // The meaning of this constant depends on the shader that was selected.
376    // See the shader assembly code above for details.
377    // Allocate one array for both registers and split it into two float4's.
378    float psConst[8] = { 0 };
379    float *multConst = &psConst[0];
380    float *addConst = &psConst[4];
381
382    switch (destFormat)
383    {
384      default: UNREACHABLE();
385      case GL_RGBA:
386      case GL_BGRA_EXT:
387        multConst[X] = 1;
388        multConst[Y] = 1;
389        multConst[Z] = 1;
390        multConst[W] = 1;
391        addConst[X] = 0;
392        addConst[Y] = 0;
393        addConst[Z] = 0;
394        addConst[W] = 0;
395        break;
396
397      case GL_RGB:
398        multConst[X] = 1;
399        multConst[Y] = 1;
400        multConst[Z] = 1;
401        multConst[W] = 0;
402        addConst[X] = 0;
403        addConst[Y] = 0;
404        addConst[Z] = 0;
405        addConst[W] = 1;
406        break;
407
408      case GL_RG_EXT:
409        multConst[X] = 1;
410        multConst[Y] = 1;
411        multConst[Z] = 0;
412        multConst[W] = 0;
413        addConst[X] = 0;
414        addConst[Y] = 0;
415        addConst[Z] = 0;
416        addConst[W] = 1;
417        break;
418
419      case GL_RED_EXT:
420        multConst[X] = 1;
421        multConst[Y] = 0;
422        multConst[Z] = 0;
423        multConst[W] = 0;
424        addConst[X] = 0;
425        addConst[Y] = 0;
426        addConst[Z] = 0;
427        addConst[W] = 1;
428        break;
429
430      case GL_ALPHA:
431        multConst[X] = 0;
432        multConst[Y] = 0;
433        multConst[Z] = 0;
434        multConst[W] = 1;
435        addConst[X] = 0;
436        addConst[Y] = 0;
437        addConst[Z] = 0;
438        addConst[W] = 0;
439        break;
440
441      case GL_LUMINANCE:
442        multConst[X] = 1;
443        multConst[Y] = 0;
444        multConst[Z] = 0;
445        multConst[W] = 0;
446        addConst[X] = 0;
447        addConst[Y] = 0;
448        addConst[Z] = 0;
449        addConst[W] = 1;
450        break;
451
452      case GL_LUMINANCE_ALPHA:
453        multConst[X] = 1;
454        multConst[Y] = 0;
455        multConst[Z] = 0;
456        multConst[W] = 1;
457        addConst[X] = 0;
458        addConst[Y] = 0;
459        addConst[Z] = 0;
460        addConst[W] = 0;
461        break;
462    }
463
464    mRenderer->getDevice()->SetPixelShaderConstantF(0, psConst, 2);
465
466    return true;
467}
468
469IDirect3DTexture9 *Blit9::copySurfaceToTexture(IDirect3DSurface9 *surface, const RECT &sourceRect)
470{
471    if (!surface)
472    {
473        return NULL;
474    }
475
476    IDirect3DDevice9 *device = mRenderer->getDevice();
477
478    D3DSURFACE_DESC sourceDesc;
479    surface->GetDesc(&sourceDesc);
480
481    // Copy the render target into a texture
482    IDirect3DTexture9 *texture;
483    HRESULT result = device->CreateTexture(sourceRect.right - sourceRect.left, sourceRect.bottom - sourceRect.top, 1, D3DUSAGE_RENDERTARGET, sourceDesc.Format, D3DPOOL_DEFAULT, &texture, NULL);
484
485    if (FAILED(result))
486    {
487        ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
488        return gl::error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
489    }
490
491    IDirect3DSurface9 *textureSurface;
492    result = texture->GetSurfaceLevel(0, &textureSurface);
493
494    if (FAILED(result))
495    {
496        ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
497        SafeRelease(texture);
498        return gl::error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
499    }
500
501    mRenderer->endScene();
502    result = device->StretchRect(surface, &sourceRect, textureSurface, NULL, D3DTEXF_NONE);
503
504    SafeRelease(textureSurface);
505
506    if (FAILED(result))
507    {
508        ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
509        SafeRelease(texture);
510        return gl::error(GL_OUT_OF_MEMORY, (IDirect3DTexture9*)NULL);
511    }
512
513    return texture;
514}
515
516void Blit9::setViewport(const RECT &sourceRect, GLint xoffset, GLint yoffset)
517{
518    IDirect3DDevice9 *device = mRenderer->getDevice();
519
520    D3DVIEWPORT9 vp;
521    vp.X      = xoffset;
522    vp.Y      = yoffset;
523    vp.Width  = sourceRect.right - sourceRect.left;
524    vp.Height = sourceRect.bottom - sourceRect.top;
525    vp.MinZ   = 0.0f;
526    vp.MaxZ   = 1.0f;
527    device->SetViewport(&vp);
528
529    float halfPixelAdjust[4] = { -1.0f/vp.Width, 1.0f/vp.Height, 0, 0 };
530    device->SetVertexShaderConstantF(0, halfPixelAdjust, 1);
531}
532
533void Blit9::setCommonBlitState()
534{
535    IDirect3DDevice9 *device = mRenderer->getDevice();
536
537    device->SetDepthStencilSurface(NULL);
538
539    device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
540    device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
541    device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
542    device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
543    device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
544    device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
545    device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
546    device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
547
548    device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
549    device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
550    device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE);
551    device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
552    device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
553
554    RECT scissorRect = {0};   // Scissoring is disabled for flipping, but we need this to capture and restore the old rectangle
555    device->SetScissorRect(&scissorRect);
556
557    for(int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
558    {
559        device->SetStreamSourceFreq(i, 1);
560    }
561}
562
563void Blit9::render()
564{
565    IDirect3DDevice9 *device = mRenderer->getDevice();
566
567    HRESULT hr = device->SetStreamSource(0, mQuadVertexBuffer, 0, 2 * sizeof(float));
568    hr = device->SetVertexDeclaration(mQuadVertexDeclaration);
569
570    mRenderer->startScene();
571    hr = device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
572}
573
574void Blit9::saveState()
575{
576    IDirect3DDevice9 *device = mRenderer->getDevice();
577
578    HRESULT hr;
579
580    device->GetDepthStencilSurface(&mSavedDepthStencil);
581    device->GetRenderTarget(0, &mSavedRenderTarget);
582
583    if (mSavedStateBlock == NULL)
584    {
585        hr = device->BeginStateBlock();
586        ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
587
588        setCommonBlitState();
589
590        static const float dummyConst[8] = { 0 };
591
592        device->SetVertexShader(NULL);
593        device->SetVertexShaderConstantF(0, dummyConst, 2);
594        device->SetPixelShader(NULL);
595        device->SetPixelShaderConstantF(0, dummyConst, 2);
596
597        D3DVIEWPORT9 dummyVp;
598        dummyVp.X = 0;
599        dummyVp.Y = 0;
600        dummyVp.Width = 1;
601        dummyVp.Height = 1;
602        dummyVp.MinZ = 0;
603        dummyVp.MaxZ = 1;
604
605        device->SetViewport(&dummyVp);
606
607        device->SetTexture(0, NULL);
608
609        device->SetStreamSource(0, mQuadVertexBuffer, 0, 0);
610
611        device->SetVertexDeclaration(mQuadVertexDeclaration);
612
613        hr = device->EndStateBlock(&mSavedStateBlock);
614        ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
615    }
616
617    ASSERT(mSavedStateBlock != NULL);
618
619    if (mSavedStateBlock != NULL)
620    {
621        hr = mSavedStateBlock->Capture();
622        ASSERT(SUCCEEDED(hr));
623    }
624}
625
626void Blit9::restoreState()
627{
628    IDirect3DDevice9 *device = mRenderer->getDevice();
629
630    device->SetDepthStencilSurface(mSavedDepthStencil);
631    SafeRelease(mSavedDepthStencil);
632
633    device->SetRenderTarget(0, mSavedRenderTarget);
634    SafeRelease(mSavedRenderTarget);
635
636    ASSERT(mSavedStateBlock != NULL);
637
638    if (mSavedStateBlock != NULL)
639    {
640        mSavedStateBlock->Apply();
641    }
642}
643
644}
645