diff options
| author | Tim Foley <tfoley@nvidia.com> | 2017-06-12 10:48:36 -0700 |
|---|---|---|
| committer | Tim Foley <tfoley@nvidia.com> | 2017-06-12 12:12:20 -0700 |
| commit | 5fe2cf3279c279750d4821a9fa97bdbbe876e568 (patch) | |
| tree | b929d3d7f77da2ba538846f598e7eeb0a302b0cc /tools/render-test/render-gl.cpp | |
| parent | 4d63b6fe73018ae253bbef0075478f5989ad279a (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/render-gl.cpp')
| -rw-r--r-- | tools/render-test/render-gl.cpp | 309 |
1 files changed, 304 insertions, 5 deletions
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; |
