diff options
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; |
