summaryrefslogtreecommitdiffstats
path: root/tools/render-test
diff options
context:
space:
mode:
authorTim Foley <tfoley@nvidia.com>2017-06-12 10:48:36 -0700
committerTim Foley <tfoley@nvidia.com>2017-06-12 12:12:20 -0700
commit5fe2cf3279c279750d4821a9fa97bdbbe876e568 (patch)
treeb929d3d7f77da2ba538846f598e7eeb0a302b0cc /tools/render-test
parent4d63b6fe73018ae253bbef0075478f5989ad279a (diff)
GLSL: get GLSL limping in `render-test`
The test case that is there right now is nominally a cross-compilation test, but for right now it uses the preprocessor to present completely different code for HLSL and GLSL compilation. This change is really just fleshing out the OpenGL side of `render-test` enough that it can produce images using OpenGL to enable further testing.
Diffstat (limited to 'tools/render-test')
-rw-r--r--tools/render-test/main.cpp19
-rw-r--r--tools/render-test/options.cpp4
-rw-r--r--tools/render-test/options.h1
-rw-r--r--tools/render-test/render-d3d11.cpp32
-rw-r--r--tools/render-test/render-gl.cpp309
-rw-r--r--tools/render-test/render.h2
-rw-r--r--tools/render-test/slang-support.cpp4
7 files changed, 359 insertions, 12 deletions
diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp
index e444a8387..2746c505d 100644
--- a/tools/render-test/main.cpp
+++ b/tools/render-test/main.cpp
@@ -262,16 +262,26 @@ int main(
// Next, we create a window using that window class.
+ // We will create a borderless window since our screen-capture logic in GL
+ // seems to get thrown off by having to deal with a window frame.
+ DWORD windowStyle = WS_POPUP;
DWORD windowExtendedStyle = 0;
- DWORD windowStyle = 0;
- LPWSTR windowName = L"Slang Hello World";
+
+
+ RECT windowRect = { 0, 0, gWindowWidth, gWindowHeight };
+ AdjustWindowRectEx(&windowRect, windowStyle, /*hasMenu=*/false, windowExtendedStyle);
+
+ auto width = windowRect.right - windowRect.left;
+ auto height = windowRect.bottom - windowRect.top;
+
+ LPWSTR windowName = L"Slang Render Test";
HWND windowHandle = CreateWindowExW(
windowExtendedStyle,
(LPWSTR)windowClassAtom,
windowName,
windowStyle,
0, 0, // x, y
- gWindowWidth, gWindowHeight,
+ width, height,
NULL, // parent
NULL, // menu
instance,
@@ -291,6 +301,7 @@ int main(
renderer = createD3D11Renderer();
break;
+ case Mode::GLSL:
case Mode::GLSLCrossCompile:
renderer = createGLRenderer();
break;
@@ -349,6 +360,8 @@ int main(
// Whenver we don't have Windows events to process,
// we render a frame.
+ static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 };
+ renderer->setClearColor(kClearColor);
renderer->clearFrame();
renderFrameInner(renderer);
diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp
index 9cfbb81fb..260e6f46c 100644
--- a/tools/render-test/options.cpp
+++ b/tools/render-test/options.cpp
@@ -56,6 +56,10 @@ void parseOptions(int* argc, char** argv)
{
gOptions.mode = Mode::HLSL;
}
+ else if( strcmp(arg, "-glsl") == 0 )
+ {
+ gOptions.mode = Mode::GLSL;
+ }
else if( strcmp(arg, "-slang") == 0 )
{
gOptions.mode = Mode::Slang;
diff --git a/tools/render-test/options.h b/tools/render-test/options.h
index ffb07bc93..81617630f 100644
--- a/tools/render-test/options.h
+++ b/tools/render-test/options.h
@@ -12,6 +12,7 @@ enum class Mode
{
Slang,
HLSL,
+ GLSL,
GLSLCrossCompile,
};
diff --git a/tools/render-test/render-d3d11.cpp b/tools/render-test/render-d3d11.cpp
index ab981bd45..4f1071905 100644
--- a/tools/render-test/render-d3d11.cpp
+++ b/tools/render-test/render-d3d11.cpp
@@ -362,6 +362,13 @@ ID3DBlob* compileHLSLShader(
flags |= D3DCOMPILE_DEBUG;
flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION;
+ // We will always define `__HLSL__` when compiling here, so that
+ // input code can react differently to being compiled as pure HLSL.
+ D3D_SHADER_MACRO defines[] = {
+ { "__HLSL__", "1" },
+ { nullptr, nullptr },
+ };
+
// The `D3DCompile` entry point takes a bunch of parameters, but we
// don't really need most of them for Slang-generated code.
ID3DBlob* dxShaderBlob = nullptr;
@@ -370,7 +377,7 @@ ID3DBlob* compileHLSLShader(
source,
strlen(source),
sourcePath,
- nullptr,
+ &defines[0],
nullptr,
entryPointName,
dxProfileName,
@@ -383,8 +390,14 @@ ID3DBlob* compileHLSLShader(
// then we will print them out (whether or not the compilation failed).
if( dxErrorBlob )
{
+ fputs(
+ (char const*)dxErrorBlob->GetBufferPointer(),
+ stderr);
+ fflush(stderr);
+
OutputDebugStringA(
(char const*)dxErrorBlob->GetBufferPointer());
+
dxErrorBlob->Release();
}
@@ -523,7 +536,13 @@ public:
DXGI_SWAP_CHAIN_DESC dxSwapChainDesc = { 0 };
dxSwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
- dxSwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
+
+ // Note(tfoley): Disabling sRGB for DX back buffer for now, so that we
+ // can get consistent output with OpenGL, where setting up sRGB will
+ // probably be more involved.
+// dxSwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
+ dxSwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+
dxSwapChainDesc.SampleDesc.Count = 1;
dxSwapChainDesc.SampleDesc.Quality = 0;
dxSwapChainDesc.BufferCount = 2;
@@ -601,12 +620,17 @@ public:
dxImmediateContext->RSSetViewports(1, &dxViewport);
}
+ float clearColor[4] = { 0, 0, 0, 0 };
+ virtual void setClearColor(float const* color) override
+ {
+ memcpy(clearColor, color, sizeof(clearColor));
+ }
+
virtual void clearFrame() override
{
- static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 };
dxImmediateContext->ClearRenderTargetView(
dxBackBufferRTV,
- kClearColor);
+ clearColor);
}
virtual void presentFrame() override
diff --git a/tools/render-test/render-gl.cpp b/tools/render-test/render-gl.cpp
index 545dd2e44..ea56a10b4 100644
--- a/tools/render-test/render-gl.cpp
+++ b/tools/render-test/render-gl.cpp
@@ -4,9 +4,12 @@
#include "options.h"
#include "render.h"
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
+#include "external/stb/stb_image_write.h"
+
// TODO(tfoley): eventually we should be able to run these
// tests on non-Windows targets to confirm that cross-compilation
// at least *works* on those platforms...
@@ -44,7 +47,18 @@
F(glGetProgramInfoLog, PFNGLGETPROGRAMINFOLOGPROC) \
F(glDeleteProgram, PFNGLDELETEPROGRAMPROC) \
F(glGetShaderInfoLog, PFNGLGETSHADERINFOLOGPROC) \
- /* emtty */
+ F(glGenBuffers, PFNGLGENBUFFERSPROC) \
+ F(glBindBuffer, PFNGLBINDBUFFERPROC) \
+ F(glBufferData, PFNGLBUFFERDATAPROC) \
+ F(glMapBuffer, PFNGLMAPBUFFERPROC) \
+ F(glUnmapBuffer, PFNGLUNMAPBUFFERPROC) \
+ F(glUseProgram, PFNGLUSEPROGRAMPROC) \
+ F(glBindBufferBase, PFNGLBINDBUFFERBASEPROC) \
+ F(glVertexAttribPointer, PFNGLVERTEXATTRIBPOINTERPROC) \
+ F(glEnableVertexAttribArray, PFNGLENABLEVERTEXATTRIBARRAYPROC) \
+ F(glDisableVertexAttribArray, PFNGLDISABLEVERTEXATTRIBARRAYPROC) \
+ F(glDebugMessageCallback, PFNGLDEBUGMESSAGECALLBACKPROC) \
+ /* end */
namespace renderer_test {
@@ -92,6 +106,60 @@ public:
#define LOAD_GL_EXTENSION_FUNC(NAME, TYPE) NAME = (TYPE) wglGetProcAddress(#NAME);
MAP_GL_EXTENSION_FUNCS(LOAD_GL_EXTENSION_FUNC)
#undef LOAD_GL_EXTENSION_FUNC
+
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ glViewport(0, 0, gWindowWidth, gWindowHeight);
+
+ if (glDebugMessageCallback)
+ {
+ glEnable(GL_DEBUG_OUTPUT);
+ glDebugMessageCallback(staticDebugCallback, this);
+ }
+ }
+
+ void debugCallback(
+ GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ GLchar const* message)
+ {
+ OutputDebugStringA("GL: ");
+ OutputDebugStringA(message);
+ OutputDebugStringA("\n");
+
+ switch (type)
+ {
+ case GL_DEBUG_TYPE_ERROR:
+ assert(!"unexpected");
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ static void APIENTRY staticDebugCallback(
+ GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ GLchar const* message,
+ void const* userParam)
+ {
+ ((GLRenderer*) userParam)->debugCallback(
+ source, type, id, severity, length, message);
+ }
+
+ float clearColor[4] = { 0, 0, 0, 0 };
+ virtual void setClearColor(float const* color) override
+ {
+ glClearColor(color[0], color[1], color[2], color[3]);
}
virtual void clearFrame() override
@@ -107,7 +175,51 @@ public:
virtual void captureScreenShot(char const* outputPath) override
{
+ int width = gWindowWidth;
+ int height = gWindowHeight;
+
+ int components = 4;
+ int rowStride = width*components;
+
+ GLubyte* buffer = (GLubyte*)malloc(components * width * height);
+
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+
+ // OpenGL's "upside down" convention bites us here, so we need
+ // to flip the data in the buffer by swapping rows
+ int halfHeight = height / 2;
+ for (int hh = 0; hh < halfHeight; ++hh)
+ {
+ // Get a pointer to the row data, and a pointer
+ // to the row on the "other end" of the image
+ GLubyte* rowA = buffer + rowStride*hh;
+ GLubyte* rowB = buffer + rowStride*(height - (hh + 1));
+ for (int ii = 0; ii < rowStride; ++ii)
+ {
+ auto a = rowA[ii];
+ auto b = rowB[ii];
+
+ rowA[ii] = b;
+ rowB[ii] = a;
+ }
+ }
+
+ //
+
+ int stbResult = stbi_write_png(
+ outputPath,
+ width,
+ height,
+ components,
+ buffer,
+ rowStride);
+ if( !stbResult )
+ {
+ assert(!"unexpected");
+ }
+
+ delete(buffer);
}
virtual ShaderCompiler* getShaderCompiler() override
@@ -117,46 +229,203 @@ public:
virtual Buffer* createBuffer(BufferDesc const& desc) override
{
- return nullptr;
+ // TODO: should derive target from desc...
+ GLenum target = GL_UNIFORM_BUFFER;
+
+ // TODO: should derive from desc...
+ GLenum usage = GL_DYNAMIC_DRAW;
+
+ GLuint bufferID = 0;
+ glGenBuffers(1, &bufferID);
+ glBindBuffer(target, bufferID);
+
+ glBufferData(target, desc.size, desc.initData, usage);
+
+ return (Buffer*)(uintptr_t)bufferID;
+ }
+
+ struct VertexAttributeFormat
+ {
+ GLint componentCount;
+ GLenum componentType;
+ GLboolean normalized;
+ };
+
+ struct VertexAttributeDesc
+ {
+ VertexAttributeFormat format;
+ GLuint streamIndex;
+ GLsizei offset;
+ };
+
+ enum
+ {
+ kMaxVertexStreams = 16,
+ };
+
+ struct InputLayoutImpl
+ {
+ VertexAttributeDesc attributes[kMaxVertexStreams];
+ UInt attributeCount = 0;
+ };
+
+ static VertexAttributeFormat getVertexAttributeFormat(
+ Format format)
+ {
+ switch (format)
+ {
+ default: assert(!"unexpected"); return VertexAttributeFormat();
+
+ #define CASE(NAME, COUNT, TYPE, NORMALIZED) \
+ case Format::NAME: do { VertexAttributeFormat result = {COUNT, TYPE, NORMALIZED}; return result; } while (0)
+
+ CASE(RGB_Float32, 3, GL_FLOAT, GL_FALSE);
+
+ #undef CASE
+
+ }
}
virtual InputLayout* createInputLayout(InputElementDesc const* inputElements, UInt inputElementCount) override
{
- return nullptr;
+ InputLayoutImpl* inputLayout = new InputLayoutImpl();
+
+ inputLayout->attributeCount = inputElementCount;
+ for (UInt ii = 0; ii < inputElementCount; ++ii)
+ {
+ auto& inputAttr = inputElements[ii];
+ auto& glAttr = inputLayout->attributes[ii];
+
+ glAttr.streamIndex = 0;
+ glAttr.format = getVertexAttributeFormat(inputAttr.format);
+ glAttr.offset = inputAttr.offset;
+ }
+
+ return (InputLayout*)inputLayout;
}
virtual void* map(Buffer* buffer, MapFlavor flavor) override
{
- return nullptr;
+ GLenum target = GL_UNIFORM_BUFFER;
+
+ GLuint access = 0;
+ switch (flavor)
+ {
+ case MapFlavor::WriteDiscard:
+ access = GL_WRITE_ONLY;
+ break;
+ }
+
+ auto bufferID = (GLuint)(uintptr_t)buffer;
+ glBindBuffer(target, bufferID);
+
+ return glMapBuffer(target, access);
}
virtual void unmap(Buffer* buffer) override
{
+ GLenum target = GL_UNIFORM_BUFFER;
+
+ auto bufferID = (GLuint)(uintptr_t)buffer;
+ glUnmapBuffer(target);
}
+ InputLayoutImpl* boundInputLayout = nullptr;
+
virtual void setInputLayout(InputLayout* inputLayout) override
{
+ boundInputLayout = (InputLayoutImpl*) inputLayout;
}
+ GLenum boundPrimitiveTopology = GL_TRIANGLES;
+
virtual void setPrimitiveTopology(PrimitiveTopology topology) override
{
+ GLenum glTopology = 0;
+ switch (topology)
+ {
+ #define CASE(NAME, VALUE) case PrimitiveTopology::NAME: glTopology = VALUE; break
+
+ CASE(TriangleList, GL_TRIANGLES);
+
+ #undef CASE
+ }
+ boundPrimitiveTopology = glTopology;
}
+ GLuint boundVertexStreamBuffers[kMaxVertexStreams];
+ UInt boundVertexStreamStrides[kMaxVertexStreams];
+ UInt boundVertexStreamOffsets[kMaxVertexStreams];
+
virtual void setVertexBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* strides, UInt const* offsets) override
{
+ for (UInt ii = 0; ii < slotCount; ++ii)
+ {
+ UInt slot = startSlot + ii;
+
+ Buffer* buffer = buffers[ii];
+ GLuint bufferID = (GLuint)(uintptr_t)buffer;
+
+ boundVertexStreamBuffers[slot] = bufferID;
+ boundVertexStreamStrides[slot] = strides[ii];
+ boundVertexStreamOffsets[slot] = offsets[ii];
+ }
}
virtual void setShaderProgram(ShaderProgram* program) override
{
+ GLuint programID = (GLuint)(uintptr_t)program;
+ glUseProgram(programID);
}
virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override
{
+ for (UInt ii = 0; ii < slotCount; ++ii)
+ {
+ UInt slot = startSlot + ii;
+
+ Buffer* buffer = buffers[ii];
+ GLuint bufferID = (GLuint)(uintptr_t)buffer;
+
+ assert(!offsets || !offsets[ii]);
+
+ glBindBufferBase(GL_UNIFORM_BUFFER, slot, bufferID);
+ }
}
+ void flushStateForDraw()
+ {
+ auto layout = this->boundInputLayout;
+ auto attrCount = layout->attributeCount;
+ for (UInt ii = 0; ii < attrCount; ++ii)
+ {
+ auto& attr = layout->attributes[ii];
+
+ auto streamIndex = attr.streamIndex;
+
+ glBindBuffer(GL_ARRAY_BUFFER, boundVertexStreamBuffers[streamIndex]);
+
+ glVertexAttribPointer(
+ ii,
+ attr.format.componentCount,
+ attr.format.componentType,
+ attr.format.normalized,
+ boundVertexStreamStrides[streamIndex],
+ (GLvoid*)(attr.offset + boundVertexStreamOffsets[streamIndex]));
+
+ glEnableVertexAttribArray(ii);
+ }
+ for (UInt ii = attrCount; ii < kMaxVertexStreams; ++ii)
+ {
+ glDisableVertexAttribArray(ii);
+ }
+ }
virtual void draw(UInt vertexCount, UInt startVertex = 0) override
{
+ flushStateForDraw();
+
+ glDrawArrays(boundPrimitiveTopology, startVertex, vertexCount);
}
// ShaderCompiler interface
@@ -204,7 +473,37 @@ public:
{
auto shaderID = glCreateShader(stage);
- glShaderSource(shaderID, 1, &source, nullptr);
+ char const* stagePrelude = "\n";
+ switch (stage)
+ {
+#define CASE(NAME) case GL_##NAME##_SHADER: stagePrelude = "#define __GLSL_" #NAME "__ 1\n"; break
+
+ CASE(VERTEX);
+ CASE(TESS_CONTROL);
+ CASE(TESS_EVALUATION);
+ CASE(GEOMETRY);
+ CASE(FRAGMENT);
+ CASE(COMPUTE);
+
+#undef CASE
+ }
+
+ char const* prelude =
+ "#define __GLSL__ 1\n"
+ ;
+
+ char const* sourceStrings[] =
+ {
+ stagePrelude,
+ prelude,
+ source,
+ };
+
+ glShaderSource(
+ shaderID,
+ sizeof(sourceStrings) / sizeof(sourceStrings[0]),
+ &sourceStrings[0],
+ nullptr);
glCompileShader(shaderID);
GLint success = GL_FALSE;
diff --git a/tools/render-test/render.h b/tools/render-test/render.h
index a30bf98f9..afa279c66 100644
--- a/tools/render-test/render.h
+++ b/tools/render-test/render.h
@@ -79,7 +79,9 @@ class Renderer
public:
virtual void initialize(void* inWindowHandle) = 0;
+ virtual void setClearColor(float const* color) = 0;
virtual void clearFrame() = 0;
+
virtual void presentFrame() = 0;
virtual void captureScreenShot(char const* outputPath) = 0;
diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp
index 5aafc562e..06a11ad4c 100644
--- a/tools/render-test/slang-support.cpp
+++ b/tools/render-test/slang-support.cpp
@@ -20,6 +20,10 @@ struct SlangShaderCompilerWrapper : public ShaderCompiler
spSetCodeGenTarget(slangRequest, target);
+ // Define a macro so that shader code in a test can detect when it is being
+ // compiled as Slang source code.
+ spAddPreprocessorDefine(slangRequest, "__SLANG__", "1");
+
int translationUnitIndex = spAddTranslationUnit(slangRequest, SLANG_SOURCE_LANGUAGE_SLANG, nullptr);
spAddTranslationUnitSourceString(slangRequest, translationUnitIndex, request.source.path, request.source.text);