Move compileToBinary to Renderer

Trac #22155
Signed-off-by: Geoff Lang
Signed-off-by: Nicolas Capens

The body of compileToBinary was moved to Renderer9 and the
ProgramBinary simply calls compileToExecutable which does both
the compilation to binary and creation of the shaders which
are then wrapped into a ShaderExecutable and then returned.

git-svn-id: https://angleproject.googlecode.com/svn/branches/dx11proto@1503 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/renderer/Renderer.h b/src/libGLESv2/renderer/Renderer.h
index 4aee4e9..ab2e253 100644
--- a/src/libGLESv2/renderer/Renderer.h
+++ b/src/libGLESv2/renderer/Renderer.h
@@ -19,6 +19,8 @@
 #include "libGLESv2/Texture.h"
 #include "libGLESv2/angletypes.h"
 
+#include "libGLESv2/renderer/ShaderExecutable.h"
+
 const int versionWindowsVista = MAKEWORD(0x00, 0x06);
 const int versionWindows7 = MAKEWORD(0x01, 0x06);
 
@@ -39,6 +41,7 @@
 
 namespace gl
 {
+class InfoLog;
 class ProgramBinary;
 class VertexAttribute;
 class Buffer;
@@ -141,6 +144,7 @@
 
     virtual GLsizei getMaxSupportedSamples() const = 0;
 
+    // Pixel operations
     virtual bool copyToRenderTarget(TextureStorage2D *dest, TextureStorage2D *source) = 0;
     virtual bool copyToRenderTarget(TextureStorageCubeMap *dest, TextureStorageCubeMap *source) = 0;
 
@@ -154,9 +158,13 @@
     virtual void readPixels(gl::Framebuffer *framebuffer, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
                             GLsizei outputPitch, bool packReverseRowOrder, GLint packAlignment, void* pixels) = 0;
 
+    // RenderTarget creation
     virtual RenderTarget *createRenderTarget(SwapChain *swapChain, bool depth) = 0;
     virtual RenderTarget *createRenderTarget(int width, int height, GLenum format, GLsizei samples, bool depth) = 0;
 
+    // Shader operations
+    virtual ShaderExecutable *compileToExecutable(gl::InfoLog &infoLog, const char *shaderHLSL, GLenum type) = 0;
+
   protected:
     egl::Display *mDisplay;
 
diff --git a/src/libGLESv2/renderer/Renderer11.cpp b/src/libGLESv2/renderer/Renderer11.cpp
index 4665b84..414208d 100644
--- a/src/libGLESv2/renderer/Renderer11.cpp
+++ b/src/libGLESv2/renderer/Renderer11.cpp
@@ -829,6 +829,13 @@
     return NULL;
 }
 
+ShaderExecutable *Renderer11::compileToExecutable(gl::InfoLog &infoLog, const char *shaderHLSL, GLenum type)
+{
+    // TODO
+    UNIMPLEMENTED();
+    return NULL;
+}
+
 bool Renderer11::blitRect(gl::Framebuffer *readTarget, gl::Rectangle *readRect, gl::Framebuffer *drawTarget, gl::Rectangle *drawRect,
                           bool blitRenderTarget, bool blitDepthStencil)
 {
diff --git a/src/libGLESv2/renderer/Renderer11.h b/src/libGLESv2/renderer/Renderer11.h
index ea32200..ef567aa 100644
--- a/src/libGLESv2/renderer/Renderer11.h
+++ b/src/libGLESv2/renderer/Renderer11.h
@@ -111,6 +111,7 @@
 
     virtual GLsizei getMaxSupportedSamples() const;
 
+    // Pixel operations
     virtual bool copyToRenderTarget(TextureStorage2D *dest, TextureStorage2D *source);
     virtual bool copyToRenderTarget(TextureStorageCubeMap *dest, TextureStorageCubeMap *source);
 
@@ -124,9 +125,13 @@
     virtual void readPixels(gl::Framebuffer *framebuffer, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
                             GLsizei outputPitch, bool packReverseRowOrder, GLint packAlignment, void* pixels);
 
+    // RenderTarget creation
     virtual RenderTarget *createRenderTarget(SwapChain *swapChain, bool depth);
     virtual RenderTarget *createRenderTarget(int width, int height, GLenum format, GLsizei samples, bool depth);
 
+    // Shader operations
+    virtual ShaderExecutable *compileToExecutable(gl::InfoLog &infoLog, const char *shaderHLSL, GLenum type);
+
     // D3D11-renderer specific methods
     ID3D11Device *getDevice() { return mDevice; }
     ID3D11DeviceContext *getDeviceContext() { return mDeviceContext; };
diff --git a/src/libGLESv2/renderer/Renderer9.cpp b/src/libGLESv2/renderer/Renderer9.cpp
index 176b0ec..f2e1350 100644
--- a/src/libGLESv2/renderer/Renderer9.cpp
+++ b/src/libGLESv2/renderer/Renderer9.cpp
@@ -18,6 +18,7 @@
 #include "libGLESv2/VertexDataManager.h"
 #include "libGLESv2/renderer/Renderer9.h"
 #include "libGLESv2/renderer/renderer9_utils.h"
+#include "libGLESv2/renderer/ShaderExecutable9.h"
 #include "libGLESv2/renderer/SwapChain9.h"
 #include "libGLESv2/renderer/TextureStorage.h"
 #include "libGLESv2/renderer/Image.h"
@@ -38,6 +39,10 @@
 #define ANGLE_ENABLE_D3D9EX 1
 #endif // !defined(ANGLE_ENABLE_D3D9EX)
 
+#if !defined(ANGLE_COMPILE_OPTIMIZATION_LEVEL)
+#define ANGLE_COMPILE_OPTIMIZATION_LEVEL D3DCOMPILE_OPTIMIZATION_LEVEL3
+#endif
+
 namespace rx
 {
 static const D3DFORMAT RenderTargetFormats[] =
@@ -2627,6 +2632,157 @@
     return renderTarget;
 }
 
+ShaderExecutable *Renderer9::compileToExecutable(gl::InfoLog &infoLog, const char *shaderHLSL, GLenum type)
+{
+    const char *profile = NULL;
+
+    switch (type)
+    {
+      case GL_VERTEX_SHADER:
+        profile = getMajorShaderModel() >= 3 ? "vs_3_0" : "vs_2_0";
+        break;
+      case GL_FRAGMENT_SHADER:
+        profile = getMajorShaderModel() >= 3 ? "ps_3_0" : "ps_2_0";
+        break;
+      default:
+        UNREACHABLE();
+        return NULL;
+    }
+
+    gl::D3DConstantTable *constantTable = NULL;
+    ID3D10Blob *binary = compileToBinary(infoLog, shaderHLSL, profile, &constantTable);
+    if (!binary)
+        return NULL;
+
+    ShaderExecutable9 *executable = NULL;
+
+    switch (type)
+    {
+      case GL_VERTEX_SHADER:
+        {
+            IDirect3DVertexShader9 *vshader = createVertexShader((DWORD *)binary->GetBufferPointer(), binary->GetBufferSize());
+            if (vshader)
+            {
+                executable = new ShaderExecutable9(vshader, constantTable);
+            }
+        }
+        break;
+      case GL_FRAGMENT_SHADER:
+        {
+            IDirect3DPixelShader9 *pshader = createPixelShader((DWORD *)binary->GetBufferPointer(), binary->GetBufferSize());
+            if (pshader)
+            {
+                executable = new ShaderExecutable9(pshader, constantTable);
+            }
+        }
+        break;
+      default:
+        UNREACHABLE();
+        break;
+    }
+
+    return executable;
+}
+
+// Compiles the HLSL code of the attached shaders into executable binaries
+ID3D10Blob *Renderer9::compileToBinary(gl::InfoLog &infoLog, const char *hlsl, const char *profile, gl::D3DConstantTable **constantTable)
+{
+    if (!hlsl)
+    {
+        return NULL;
+    }
+
+    HRESULT result = S_OK;
+    UINT flags = 0;
+    std::string sourceText;
+    if (gl::perfActive())
+    {
+        flags |= D3DCOMPILE_DEBUG;
+#ifdef NDEBUG
+        flags |= ANGLE_COMPILE_OPTIMIZATION_LEVEL;
+#else
+        flags |= D3DCOMPILE_SKIP_OPTIMIZATION;
+#endif
+
+        std::string sourcePath = getTempPath();
+        sourceText = std::string("#line 2 \"") + sourcePath + std::string("\"\n\n") + std::string(hlsl);
+        writeFile(sourcePath.c_str(), sourceText.c_str(), sourceText.size());
+    }
+    else
+    {
+        flags |= ANGLE_COMPILE_OPTIMIZATION_LEVEL;
+        sourceText = hlsl;
+    }
+
+    // Sometimes D3DCompile will fail with the default compilation flags for complicated shaders when it would otherwise pass with alternative options.
+    // Try the default flags first and if compilation fails, try some alternatives.
+    const static UINT extraFlags[] =
+    {
+        0,
+        D3DCOMPILE_AVOID_FLOW_CONTROL,
+        D3DCOMPILE_PREFER_FLOW_CONTROL
+    };
+
+    const static char * const extraFlagNames[] =
+    {
+        "default",
+        "avoid flow control",
+        "prefer flow control"
+    };
+
+    for (int i = 0; i < sizeof(extraFlags) / sizeof(UINT); ++i)
+    {
+        ID3D10Blob *errorMessage = NULL;
+        ID3D10Blob *binary = NULL;
+        result = compileShaderSource(hlsl, gl::g_fakepath, profile, flags | extraFlags[i], &binary, &errorMessage);
+
+        if (errorMessage)
+        {
+            const char *message = (const char*)errorMessage->GetBufferPointer();
+
+            infoLog.appendSanitized(message);
+            TRACE("\n%s", hlsl);
+            TRACE("\n%s", message);
+
+            errorMessage->Release();
+            errorMessage = NULL;
+        }
+
+        if (SUCCEEDED(result))
+        {
+            gl::D3DConstantTable *table = new gl::D3DConstantTable(binary->GetBufferPointer(), binary->GetBufferSize());
+            if (table->error())
+            {
+                delete table;
+                binary->Release();
+                return NULL;
+            }
+
+            *constantTable = table;
+            return binary;
+        }
+        else
+        {
+            if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
+            {
+                return error(GL_OUT_OF_MEMORY, (ID3D10Blob*) NULL);
+            }
+
+            infoLog.append("Warning: D3D shader compilation failed with ");
+            infoLog.append(extraFlagNames[i]);
+            infoLog.append(" flags.");
+            if (i + 1 < sizeof(extraFlagNames) / sizeof(char*))
+            {
+                infoLog.append(" Retrying with ");
+                infoLog.append(extraFlagNames[i + 1]);
+                infoLog.append(".\n");
+            }
+        }
+    }
+
+    return NULL;
+}
+
 bool Renderer9::boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest)
 {
     return mBlit->boxFilter(source, dest);
diff --git a/src/libGLESv2/renderer/Renderer9.h b/src/libGLESv2/renderer/Renderer9.h
index eca55e2..f07cd1c 100644
--- a/src/libGLESv2/renderer/Renderer9.h
+++ b/src/libGLESv2/renderer/Renderer9.h
@@ -31,6 +31,7 @@
 
 namespace gl
 {
+class D3DConstantTable;
 class VertexDataManager;
 class StreamingIndexBuffer;
 struct TranslatedAttribute;
@@ -151,6 +152,7 @@
     
     D3DFORMAT ConvertTextureInternalFormat(GLint internalformat);
 
+    // Pixel operations
     virtual bool copyToRenderTarget(TextureStorage2D *dest, TextureStorage2D *source);
     virtual bool copyToRenderTarget(TextureStorageCubeMap *dest, TextureStorageCubeMap *source);
 
@@ -164,9 +166,14 @@
     virtual void readPixels(gl::Framebuffer *framebuffer, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
                             GLsizei outputPitch, bool packReverseRowOrder, GLint packAlignment, void* pixels);
 
+    // RenderTarget creation
     virtual RenderTarget *createRenderTarget(SwapChain *swapChain, bool depth);
     virtual RenderTarget *createRenderTarget(int width, int height, GLenum format, GLsizei samples, bool depth);
 
+    // Shader operations
+    virtual ShaderExecutable *compileToExecutable(gl::InfoLog &infoLog, const char *shaderHLSL, GLenum type);
+
+    // D3D9-renderer specific methods
     bool boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest);
 
     D3DPOOL getTexturePool(DWORD usage) const;
@@ -179,6 +186,8 @@
     void getMultiSampleSupport(D3DFORMAT format, bool *multiSampleArray);
     bool copyToRenderTarget(IDirect3DSurface9 *dest, IDirect3DSurface9 *source, bool fromManaged);
 
+    ID3D10Blob *compileToBinary(gl::InfoLog &infoLog, const char *hlsl, const char *profile, gl::D3DConstantTable **constantTable);
+
     D3DPOOL getBufferPool(DWORD usage) const;
 
     HMODULE mD3d9Module;