Adds renderer functions for rendertarget operations.

TRAC #22093

Signed-off-by: Geoff Lang
Signed-off-by: Daniel Koch

git-svn-id: https://angleproject.googlecode.com/svn/branches/dx11proto@1444 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/renderer/Renderer9.cpp b/src/libGLESv2/renderer/Renderer9.cpp
index 2dd6116..ab8f510 100644
--- a/src/libGLESv2/renderer/Renderer9.cpp
+++ b/src/libGLESv2/renderer/Renderer9.cpp
@@ -1438,6 +1438,435 @@
     return mBlit->copy(framebuffer, sourceRect, destFormat, xoffset, yoffset, storage, target, level);
 }
 
+bool Renderer9::blitRect(gl::Framebuffer *readFramebuffer, gl::Rectangle *readRect, gl::Framebuffer *drawFramebuffer, gl::Rectangle *drawRect,
+                         bool blitRenderTarget, bool blitDepthStencil)
+{
+    endScene();
+
+    if (blitRenderTarget)
+    {
+        IDirect3DSurface9* readRenderTarget = readFramebuffer->getRenderTarget();
+        IDirect3DSurface9* drawRenderTarget = drawFramebuffer->getRenderTarget();
+
+        RECT srcRect, dstRect;
+        RECT *srcRectPtr = NULL;
+        RECT *dstRectPtr = NULL;
+
+        if (readRect)
+        {
+            srcRect.left = readRect->x;
+            srcRect.right = readRect->x + readRect->width;
+            srcRect.top = readRect->y;
+            srcRect.bottom = readRect->y + readRect->height;
+            srcRectPtr = &srcRect;
+        }
+
+        if (drawRect)
+        {
+            dstRect.left = drawRect->x;
+            dstRect.right = drawRect->x + drawRect->width;
+            dstRect.top = drawRect->y;
+            dstRect.bottom = drawRect->y + drawRect->height;
+            dstRectPtr = &dstRect;
+        }
+
+        HRESULT result = mDevice->StretchRect(readRenderTarget, srcRectPtr, drawRenderTarget, dstRectPtr, D3DTEXF_NONE);
+
+        readRenderTarget->Release();
+        drawRenderTarget->Release();
+
+        if (FAILED(result))
+        {
+            ERR("BlitFramebufferANGLE failed: StretchRect returned %x.", result);
+            return false;
+        }
+    }
+
+    if (blitDepthStencil)
+    {
+        IDirect3DSurface9* readDepthStencil = readFramebuffer->getDepthStencil();
+        IDirect3DSurface9* drawDepthStencil = drawFramebuffer->getDepthStencil();
+
+        HRESULT result = mDevice->StretchRect(readDepthStencil, NULL, drawDepthStencil, NULL, D3DTEXF_NONE);
+
+        readDepthStencil->Release();
+        drawDepthStencil->Release();
+
+        if (FAILED(result))
+        {
+            ERR("BlitFramebufferANGLE failed: StretchRect returned %x.", result);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void Renderer9::readPixels(gl::Framebuffer *framebuffer, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, 
+                           GLsizei outputPitch, bool packReverseRowOrder, GLint packAlignment, void* pixels)
+{
+    IDirect3DSurface9 *renderTarget = framebuffer->getRenderTarget();
+    if (!renderTarget)
+    {
+        return;   // Context must be lost, return silently
+    }
+
+    D3DSURFACE_DESC desc;
+    renderTarget->GetDesc(&desc);
+
+    if (desc.MultiSampleType != D3DMULTISAMPLE_NONE)
+    {
+        UNIMPLEMENTED();   // FIXME: Requires resolve using StretchRect into non-multisampled render target
+        renderTarget->Release();
+        return error(GL_OUT_OF_MEMORY);
+    }
+
+    HRESULT result;
+    IDirect3DSurface9 *systemSurface = NULL;
+    bool directToPixels = !packReverseRowOrder && packAlignment <= 4 && getShareHandleSupport() &&
+                          x == 0 && y == 0 && UINT(width) == desc.Width && UINT(height) == desc.Height &&
+                          desc.Format == D3DFMT_A8R8G8B8 && format == GL_BGRA_EXT && type == GL_UNSIGNED_BYTE;
+    if (directToPixels)
+    {
+        // Use the pixels ptr as a shared handle to write directly into client's memory
+        result = mDevice->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format,
+                                                      D3DPOOL_SYSTEMMEM, &systemSurface, &pixels);
+        if (FAILED(result))
+        {
+            // Try again without the shared handle
+            directToPixels = false;
+        }
+    }
+
+    if (!directToPixels)
+    {
+        result = mDevice->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format,
+                                                      D3DPOOL_SYSTEMMEM, &systemSurface, NULL);
+        if (FAILED(result))
+        {
+            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
+            renderTarget->Release();
+            return error(GL_OUT_OF_MEMORY);
+        }
+    }
+
+    result = mDevice->GetRenderTargetData(renderTarget, systemSurface);
+    renderTarget->Release();
+    renderTarget = NULL;
+
+    if (FAILED(result))
+    {
+        systemSurface->Release();
+
+        // It turns out that D3D will sometimes produce more error
+        // codes than those documented.
+        if (gl::checkDeviceLost(result))
+            return error(GL_OUT_OF_MEMORY);
+        else
+        {
+            UNREACHABLE();
+            return;
+        }
+
+    }
+
+    if (directToPixels)
+    {
+        systemSurface->Release();
+        return;
+    }
+
+    RECT rect;
+    rect.left = gl::clamp(x, 0L, static_cast<LONG>(desc.Width));
+    rect.top = gl::clamp(y, 0L, static_cast<LONG>(desc.Height));
+    rect.right = gl::clamp(x + width, 0L, static_cast<LONG>(desc.Width));
+    rect.bottom = gl::clamp(y + height, 0L, static_cast<LONG>(desc.Height));
+
+    D3DLOCKED_RECT lock;
+    result = systemSurface->LockRect(&lock, &rect, D3DLOCK_READONLY);
+
+    if (FAILED(result))
+    {
+        UNREACHABLE();
+        systemSurface->Release();
+
+        return;   // No sensible error to generate
+    }
+
+    unsigned char *dest = (unsigned char*)pixels;
+    unsigned short *dest16 = (unsigned short*)pixels;
+
+    unsigned char *source;
+    int inputPitch;
+    if (packReverseRowOrder)
+    {
+        source = ((unsigned char*)lock.pBits) + lock.Pitch * (rect.bottom - rect.top - 1);
+        inputPitch = -lock.Pitch;
+    }
+    else
+    {
+        source = (unsigned char*)lock.pBits;
+        inputPitch = lock.Pitch;
+    }
+
+    unsigned int fastPixelSize = 0;
+
+    if (desc.Format == D3DFMT_A8R8G8B8 &&
+        format == GL_BGRA_EXT &&
+        type == GL_UNSIGNED_BYTE)
+    {
+        fastPixelSize = 4;
+    }
+    else if ((desc.Format == D3DFMT_A4R4G4B4 &&
+             format == GL_BGRA_EXT &&
+             type == GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT) ||
+             (desc.Format == D3DFMT_A1R5G5B5 &&
+             format == GL_BGRA_EXT &&
+             type == GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT))
+    {
+        fastPixelSize = 2;
+    }
+    else if (desc.Format == D3DFMT_A16B16G16R16F &&
+             format == GL_RGBA &&
+             type == GL_HALF_FLOAT_OES)
+    {
+        fastPixelSize = 8;
+    }
+    else if (desc.Format == D3DFMT_A32B32G32R32F &&
+             format == GL_RGBA &&
+             type == GL_FLOAT)
+    {
+        fastPixelSize = 16;
+    }
+
+    for (int j = 0; j < rect.bottom - rect.top; j++)
+    {
+        if (fastPixelSize != 0)
+        {
+            // Fast path for formats which require no translation:
+            // D3DFMT_A8R8G8B8 to BGRA/UNSIGNED_BYTE
+            // D3DFMT_A4R4G4B4 to BGRA/UNSIGNED_SHORT_4_4_4_4_REV_EXT
+            // D3DFMT_A1R5G5B5 to BGRA/UNSIGNED_SHORT_1_5_5_5_REV_EXT
+            // D3DFMT_A16B16G16R16F to RGBA/HALF_FLOAT_OES
+            // D3DFMT_A32B32G32R32F to RGBA/FLOAT
+            // 
+            // Note that buffers with no alpha go through the slow path below.
+            memcpy(dest + j * outputPitch,
+                   source + j * inputPitch,
+                   (rect.right - rect.left) * fastPixelSize);
+            continue;
+        }
+
+        for (int i = 0; i < rect.right - rect.left; i++)
+        {
+            float r;
+            float g;
+            float b;
+            float a;
+
+            switch (desc.Format)
+            {
+              case D3DFMT_R5G6B5:
+                {
+                    unsigned short rgb = *(unsigned short*)(source + 2 * i + j * inputPitch);
+
+                    a = 1.0f;
+                    b = (rgb & 0x001F) * (1.0f / 0x001F);
+                    g = (rgb & 0x07E0) * (1.0f / 0x07E0);
+                    r = (rgb & 0xF800) * (1.0f / 0xF800);
+                }
+                break;
+              case D3DFMT_A1R5G5B5:
+                {
+                    unsigned short argb = *(unsigned short*)(source + 2 * i + j * inputPitch);
+
+                    a = (argb & 0x8000) ? 1.0f : 0.0f;
+                    b = (argb & 0x001F) * (1.0f / 0x001F);
+                    g = (argb & 0x03E0) * (1.0f / 0x03E0);
+                    r = (argb & 0x7C00) * (1.0f / 0x7C00);
+                }
+                break;
+              case D3DFMT_A8R8G8B8:
+                {
+                    unsigned int argb = *(unsigned int*)(source + 4 * i + j * inputPitch);
+
+                    a = (argb & 0xFF000000) * (1.0f / 0xFF000000);
+                    b = (argb & 0x000000FF) * (1.0f / 0x000000FF);
+                    g = (argb & 0x0000FF00) * (1.0f / 0x0000FF00);
+                    r = (argb & 0x00FF0000) * (1.0f / 0x00FF0000);
+                }
+                break;
+              case D3DFMT_X8R8G8B8:
+                {
+                    unsigned int xrgb = *(unsigned int*)(source + 4 * i + j * inputPitch);
+
+                    a = 1.0f;
+                    b = (xrgb & 0x000000FF) * (1.0f / 0x000000FF);
+                    g = (xrgb & 0x0000FF00) * (1.0f / 0x0000FF00);
+                    r = (xrgb & 0x00FF0000) * (1.0f / 0x00FF0000);
+                }
+                break;
+              case D3DFMT_A2R10G10B10:
+                {
+                    unsigned int argb = *(unsigned int*)(source + 4 * i + j * inputPitch);
+
+                    a = (argb & 0xC0000000) * (1.0f / 0xC0000000);
+                    b = (argb & 0x000003FF) * (1.0f / 0x000003FF);
+                    g = (argb & 0x000FFC00) * (1.0f / 0x000FFC00);
+                    r = (argb & 0x3FF00000) * (1.0f / 0x3FF00000);
+                }
+                break;
+              case D3DFMT_A32B32G32R32F:
+                {
+                    // float formats in D3D are stored rgba, rather than the other way round
+                    r = *((float*)(source + 16 * i + j * inputPitch) + 0);
+                    g = *((float*)(source + 16 * i + j * inputPitch) + 1);
+                    b = *((float*)(source + 16 * i + j * inputPitch) + 2);
+                    a = *((float*)(source + 16 * i + j * inputPitch) + 3);
+                }
+                break;
+              case D3DFMT_A16B16G16R16F:
+                {
+                    // float formats in D3D are stored rgba, rather than the other way round
+                    r = gl::float16ToFloat32(*((unsigned short*)(source + 8 * i + j * inputPitch) + 0));
+                    g = gl::float16ToFloat32(*((unsigned short*)(source + 8 * i + j * inputPitch) + 1));
+                    b = gl::float16ToFloat32(*((unsigned short*)(source + 8 * i + j * inputPitch) + 2));
+                    a = gl::float16ToFloat32(*((unsigned short*)(source + 8 * i + j * inputPitch) + 3));
+                }
+                break;
+              default:
+                UNIMPLEMENTED();   // FIXME
+                UNREACHABLE();
+                return;
+            }
+
+            switch (format)
+            {
+              case GL_RGBA:
+                switch (type)
+                {
+                  case GL_UNSIGNED_BYTE:
+                    dest[4 * i + j * outputPitch + 0] = (unsigned char)(255 * r + 0.5f);
+                    dest[4 * i + j * outputPitch + 1] = (unsigned char)(255 * g + 0.5f);
+                    dest[4 * i + j * outputPitch + 2] = (unsigned char)(255 * b + 0.5f);
+                    dest[4 * i + j * outputPitch + 3] = (unsigned char)(255 * a + 0.5f);
+                    break;
+                  default: UNREACHABLE();
+                }
+                break;
+              case GL_BGRA_EXT:
+                switch (type)
+                {
+                  case GL_UNSIGNED_BYTE:
+                    dest[4 * i + j * outputPitch + 0] = (unsigned char)(255 * b + 0.5f);
+                    dest[4 * i + j * outputPitch + 1] = (unsigned char)(255 * g + 0.5f);
+                    dest[4 * i + j * outputPitch + 2] = (unsigned char)(255 * r + 0.5f);
+                    dest[4 * i + j * outputPitch + 3] = (unsigned char)(255 * a + 0.5f);
+                    break;
+                  case GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT:
+                    // According to the desktop GL spec in the "Transfer of Pixel Rectangles" section
+                    // this type is packed as follows:
+                    //   15   14   13   12   11   10    9    8    7    6    5    4    3    2    1    0
+                    //  --------------------------------------------------------------------------------
+                    // |       4th         |        3rd         |        2nd        |   1st component   |
+                    //  --------------------------------------------------------------------------------
+                    // in the case of BGRA_EXT, B is the first component, G the second, and so forth.
+                    dest16[i + j * outputPitch / sizeof(unsigned short)] =
+                        ((unsigned short)(15 * a + 0.5f) << 12)|
+                        ((unsigned short)(15 * r + 0.5f) << 8) |
+                        ((unsigned short)(15 * g + 0.5f) << 4) |
+                        ((unsigned short)(15 * b + 0.5f) << 0);
+                    break;
+                  case GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT:
+                    // According to the desktop GL spec in the "Transfer of Pixel Rectangles" section
+                    // this type is packed as follows:
+                    //   15   14   13   12   11   10    9    8    7    6    5    4    3    2    1    0
+                    //  --------------------------------------------------------------------------------
+                    // | 4th |          3rd           |           2nd          |      1st component     |
+                    //  --------------------------------------------------------------------------------
+                    // in the case of BGRA_EXT, B is the first component, G the second, and so forth.
+                    dest16[i + j * outputPitch / sizeof(unsigned short)] =
+                        ((unsigned short)(     a + 0.5f) << 15) |
+                        ((unsigned short)(31 * r + 0.5f) << 10) |
+                        ((unsigned short)(31 * g + 0.5f) << 5) |
+                        ((unsigned short)(31 * b + 0.5f) << 0);
+                    break;
+                  default: UNREACHABLE();
+                }
+                break;
+              case GL_RGB:
+                switch (type)
+                {
+                  case GL_UNSIGNED_SHORT_5_6_5:
+                    dest16[i + j * outputPitch / sizeof(unsigned short)] = 
+                        ((unsigned short)(31 * b + 0.5f) << 0) |
+                        ((unsigned short)(63 * g + 0.5f) << 5) |
+                        ((unsigned short)(31 * r + 0.5f) << 11);
+                    break;
+                  case GL_UNSIGNED_BYTE:
+                    dest[3 * i + j * outputPitch + 0] = (unsigned char)(255 * r + 0.5f);
+                    dest[3 * i + j * outputPitch + 1] = (unsigned char)(255 * g + 0.5f);
+                    dest[3 * i + j * outputPitch + 2] = (unsigned char)(255 * b + 0.5f);
+                    break;
+                  default: UNREACHABLE();
+                }
+                break;
+              default: UNREACHABLE();
+            }
+        }
+    }
+
+    systemSurface->UnlockRect();
+
+    systemSurface->Release();
+}
+
+bool Renderer9::setRenderTarget(gl::Renderbuffer *renderbuffer)
+{
+    IDirect3DSurface9 *renderTarget = NULL;
+    
+    if (renderbuffer)
+    {
+        renderTarget = renderbuffer->getRenderTarget();
+        if (!renderTarget)
+        {
+            ERR("render target pointer unexpectedly null.");
+            return false;   // Context must be lost
+        }
+
+        mDevice->SetRenderTarget(0, renderTarget);
+        renderTarget->Release();
+    }
+    else
+    {
+        mDevice->SetRenderTarget(0, NULL);
+    }
+
+    return true;
+}
+
+bool Renderer9::setDepthStencil(gl::Renderbuffer *renderbuffer)
+{
+    IDirect3DSurface9 *depthStencil = NULL;
+    
+    if (renderbuffer)
+    {
+        depthStencil = renderbuffer->getDepthStencil();
+        if (!depthStencil)
+        {
+            ERR("depth stencil pointer unexpectedly null.");
+            return false;   // Context must be lost
+        }
+        mDevice->SetDepthStencilSurface(depthStencil);
+        depthStencil->Release();
+    }
+    else
+    {
+        mDevice->SetDepthStencilSurface(NULL);
+    }
+    return true;
+}
+
 bool Renderer9::boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest)
 {
     return mBlit->boxFilter(source, dest);