Adds support for per-framebuffer IMPLEMENTATION_COLOR_READ_FORMAT and TYPE

TRAC #21607

Signed-off-by: Nicolas Capens
Signed-off-by: Daniel Koch

Author:    Shannon Woods

git-svn-id: https://angleproject.googlecode.com/svn/trunk@1283 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index 76f0693..1363ed6 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -1576,8 +1576,19 @@
             }
         }
         break;
-      case GL_IMPLEMENTATION_COLOR_READ_TYPE:   *params = gl::IMPLEMENTATION_COLOR_READ_TYPE;   break;
-      case GL_IMPLEMENTATION_COLOR_READ_FORMAT: *params = gl::IMPLEMENTATION_COLOR_READ_FORMAT; break;
+      case GL_IMPLEMENTATION_COLOR_READ_TYPE:
+      case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
+        {
+            GLenum format, type;
+            if (getCurrentReadFormatType(&format, &type))
+            {
+                if (pname == GL_IMPLEMENTATION_COLOR_READ_FORMAT)
+                    *params = format;
+                else
+                    *params = type;
+            }
+        }
+        break;
       case GL_MAX_VIEWPORT_DIMS:
         {
             int maxDimension = std::max(getMaximumRenderbufferDimension(), getMaximumTextureDimension());
@@ -2611,18 +2622,51 @@
         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 (desc.Format == D3DFMT_A8R8G8B8 &&
-            format == GL_BGRA_EXT &&
-            type == GL_UNSIGNED_BYTE)
+        if (fastPixelSize != 0)
         {
-            // Fast path for EXT_read_format_bgra, given
-            // an RGBA source buffer.  Note that buffers with no
-            // alpha go through the slow path below.
+            // 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) * 4);
+                   (rect.right - rect.left) * fastPixelSize);
             continue;
         }
 
@@ -2763,15 +2807,20 @@
                   default: UNREACHABLE();
                 }
                 break;
-              case GL_RGB:   // IMPLEMENTATION_COLOR_READ_FORMAT
+              case GL_RGB:
                 switch (type)
                 {
-                  case GL_UNSIGNED_SHORT_5_6_5:   // IMPLEMENTATION_COLOR_READ_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;
@@ -3581,6 +3630,29 @@
     return mMaxTextureAnisotropy;
 }
 
+bool Context::getCurrentReadFormatType(GLenum *format, GLenum *type)
+{
+    Framebuffer *framebuffer = getReadFramebuffer();
+    if (!framebuffer || framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
+    {
+        return error(GL_INVALID_OPERATION, false);
+    }
+
+    Renderbuffer *renderbuffer = framebuffer->getColorbuffer();
+    if (!renderbuffer)
+    {
+        return error(GL_INVALID_OPERATION, false);
+    }
+
+    if(!dx2es::ConvertReadBufferFormat(renderbuffer->getD3DFormat(), format, type))
+    {
+        ASSERT(false);
+        return false;
+    }
+
+    return true;
+}
+
 void Context::detachBuffer(GLuint buffer)
 {
     // [OpenGL ES 2.0.24] section 2.9 page 22:
diff --git a/src/libGLESv2/Context.h b/src/libGLESv2/Context.h
index c3b562c..d312918 100644
--- a/src/libGLESv2/Context.h
+++ b/src/libGLESv2/Context.h
@@ -78,10 +78,7 @@
     MAX_COMBINED_TEXTURE_IMAGE_UNITS_VTF = MAX_TEXTURE_IMAGE_UNITS + MAX_VERTEX_TEXTURE_IMAGE_UNITS_VTF,    
     MAX_FRAGMENT_UNIFORM_VECTORS_SM2 = 32 - 3,    // Reserve space for dx_Coord, dx_Depth, and dx_DepthRange. dx_PointOrLines and dx_FrontCCW use separate bool registers.
     MAX_FRAGMENT_UNIFORM_VECTORS_SM3 = 224 - 3,
-    MAX_DRAW_BUFFERS = 1,
-
-    IMPLEMENTATION_COLOR_READ_FORMAT = GL_RGB,
-    IMPLEMENTATION_COLOR_READ_TYPE = GL_UNSIGNED_SHORT_5_6_5
+    MAX_DRAW_BUFFERS = 1
 };
 
 enum QueryType
@@ -510,6 +507,8 @@
     bool supportsInstancing() const;
     bool supportsTextureFilterAnisotropy() const;
 
+    bool getCurrentReadFormatType(GLenum *format, GLenum *type);
+
     float getTextureMaxAnisotropy() const;
 
     void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, 
diff --git a/src/libGLESv2/libGLESv2.cpp b/src/libGLESv2/libGLESv2.cpp
index 40a7067..62bf20f 100644
--- a/src/libGLESv2/libGLESv2.cpp
+++ b/src/libGLESv2/libGLESv2.cpp
@@ -247,15 +247,6 @@
             return false;
         }
         break;
-      case gl::IMPLEMENTATION_COLOR_READ_FORMAT:
-        switch (type)
-        {
-          case gl::IMPLEMENTATION_COLOR_READ_TYPE:
-            break;
-          default:
-            return false;
-        }
-        break;
       default:
         return false;
     }
@@ -4720,15 +4711,23 @@
             return error(GL_INVALID_VALUE);
         }
 
-        if (!validReadFormatType(format, type))
-        {
-            return error(GL_INVALID_OPERATION);
-        }
-
         gl::Context *context = gl::getNonLostContext();
 
         if (context)
         {
+            GLenum currentFormat, currentType;
+    
+            // Failure in getCurrentReadFormatType indicates that no color attachment is currently bound,
+            // and attempting to read back if that's the case is an error. The error will be registered
+            // by getCurrentReadFormat.
+            if (!context->getCurrentReadFormatType(&currentFormat, &currentType))
+                return;
+
+            if (!(currentFormat == format && currentType == type) && !validReadFormatType(format, type))
+            {
+                return error(GL_INVALID_OPERATION);
+            }
+
             context->readPixels(x, y, width, height, format, type, &bufSize, data);
         }
     }
@@ -4752,15 +4751,23 @@
             return error(GL_INVALID_VALUE);
         }
 
-        if (!validReadFormatType(format, type))
-        {
-            return error(GL_INVALID_OPERATION);
-        }
-
         gl::Context *context = gl::getNonLostContext();
 
         if (context)
         {
+            GLenum currentFormat, currentType;
+    
+            // Failure in getCurrentReadFormatType indicates that no color attachment is currently bound,
+            // and attempting to read back if that's the case is an error. The error will be registered
+            // by getCurrentReadFormat.
+            if (!context->getCurrentReadFormatType(&currentFormat, &currentType))
+                return;
+
+            if (!(currentFormat == format && currentType == type) && !validReadFormatType(format, type))
+            {
+                return error(GL_INVALID_OPERATION);
+            }
+
             context->readPixels(x, y, width, height, format, type, NULL, pixels);
         }
     }
diff --git a/src/libGLESv2/utilities.cpp b/src/libGLESv2/utilities.cpp
index e6f8993..47d2379 100644
--- a/src/libGLESv2/utilities.cpp
+++ b/src/libGLESv2/utilities.cpp
@@ -988,6 +988,46 @@
         return type;
 }
 
+bool ConvertReadBufferFormat(D3DFORMAT d3dformat, GLenum *format, GLenum *type)
+{
+    switch (d3dformat)
+    {
+      case D3DFMT_A8R8G8B8:
+        *type = GL_UNSIGNED_BYTE;
+        *format = GL_BGRA_EXT;
+        break;
+      case D3DFMT_X8R8G8B8:
+        *type = GL_UNSIGNED_BYTE;
+        *format = GL_RGB;
+        break;
+      case D3DFMT_R5G6B5:
+        *type = GL_UNSIGNED_SHORT_5_6_5;
+        *format = GL_RGB;
+        break;
+      case D3DFMT_A16B16G16R16F:
+        *type = GL_HALF_FLOAT_OES;
+        *format = GL_RGBA;
+        break;
+      case D3DFMT_A32B32G32R32F:
+        *type = GL_FLOAT;
+        *format = GL_RGBA;
+        break;
+      case D3DFMT_A4R4G4B4:
+        *type = GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT;
+        *format = GL_BGRA_EXT;
+        break;
+      case D3DFMT_A1R5G5B5:
+        *type = GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT;
+        *format = GL_BGRA_EXT;
+        break;
+      default:
+        *type = GL_NONE;
+        *format = GL_NONE;
+        return false;
+    }
+    return true;
+}
+
 GLenum ConvertBackBufferFormat(D3DFORMAT format)
 {
     switch (format)
diff --git a/src/libGLESv2/utilities.h b/src/libGLESv2/utilities.h
index 3357eb0..3e270c0 100644
--- a/src/libGLESv2/utilities.h
+++ b/src/libGLESv2/utilities.h
@@ -88,6 +88,7 @@
 
 GLsizei GetSamplesFromMultisampleType(D3DMULTISAMPLE_TYPE type);
 
+bool ConvertReadBufferFormat(D3DFORMAT d3dformat, GLenum *format, GLenum *type);
 GLenum ConvertBackBufferFormat(D3DFORMAT format);
 GLenum ConvertDepthStencilFormat(D3DFORMAT format);