diff options
| -rw-r--r-- | tests/render/cross-compile0.hlsl | 73 | ||||
| -rw-r--r-- | tools/render-test/main.cpp | 19 | ||||
| -rw-r--r-- | tools/render-test/options.cpp | 4 | ||||
| -rw-r--r-- | tools/render-test/options.h | 1 | ||||
| -rw-r--r-- | tools/render-test/render-d3d11.cpp | 32 | ||||
| -rw-r--r-- | tools/render-test/render-gl.cpp | 309 | ||||
| -rw-r--r-- | tools/render-test/render.h | 2 | ||||
| -rw-r--r-- | tools/render-test/slang-support.cpp | 4 | ||||
| -rw-r--r-- | tools/slang-test/main.cpp | 6 |
9 files changed, 433 insertions, 17 deletions
diff --git a/tests/render/cross-compile0.hlsl b/tests/render/cross-compile0.hlsl index 9a9dd1cdc..e41de7ef1 100644 --- a/tests/render/cross-compile0.hlsl +++ b/tests/render/cross-compile0.hlsl @@ -1,12 +1,17 @@ -//TEST:COMPARE_HLSL_CROSS_COMPILE_RENDER: +//TEST:COMPARE_HLSL_GLSL_RENDER: -// Now we are going to test that we can cross-compile a Spire/HLSL -// input file over to GLSL and render with it. +// This is a basic test case for cross-compilation behavior. +// +// We will define distinct HLSL and GLSL entry points, +// but the two will share a dependency on a file of +// pure Spire code that provides the actual shading logic. + +#if defined(__HLSL__) cbuffer Uniforms { float4x4 modelViewProjection; -} +}; struct AssembledVertex { @@ -24,7 +29,6 @@ struct Fragment float4 color; }; - // Vertex Shader struct VertexStageInput @@ -49,6 +53,7 @@ VertexStageOutput vertexMain(VertexStageInput input) output.sv_position = mul(modelViewProjection, float4(position, 1.0)); return output; + } // Fragment Shader @@ -74,3 +79,61 @@ FragmentStageOutput fragmentMain(FragmentStageInput input) return output; } +#elif defined(__GLSL__) + +#version 420 + +uniform Uniforms +{ + mat4x4 modelViewProjection; +}; + +#define ASSEMBLED_VERTEX(QUAL) \ + /* */ + +#define V2F(QUAL) \ + QUAL vec3 coarse_color; \ + /* */ + +// Vertex Shader + +#ifdef __GLSL_VERTEX__ + +layout(location = 0) +in vec3 assembled_position; + +layout(location = 1) +in vec3 assembled_color; + +V2F(out) + +void main() +{ + vec3 position = assembled_position; + vec3 color = assembled_color; + + coarse_color = color; +// gl_Position = modelViewProjection * vec4(position, 1.0); + gl_Position = vec4(position, 1.0) * modelViewProjection; +} + +#endif + +#ifdef __GLSL_FRAGMENT__ + +V2F(in) + +layout(location = 0) +out vec4 fragment_color; + +void main() +{ + vec3 color = coarse_color; + + fragment_color = vec4(color, 1.0); +} + + +#endif + +#endif 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); diff --git a/tools/slang-test/main.cpp b/tools/slang-test/main.cpp index d8a04a050..37a81ef92 100644 --- a/tools/slang-test/main.cpp +++ b/tools/slang-test/main.cpp @@ -842,6 +842,11 @@ TestResult runHLSLCrossCompileRenderComparisonTest(TestInput& input) return runHLSLRenderComparisonTestImpl(input, "-slang", "-glsl-cross"); } +TestResult runHLSLAndGLSLComparisonTest(TestInput& input) +{ + return runHLSLRenderComparisonTestImpl(input, "-hlsl", "-glsl"); +} + TestResult runTest( String const& filePath, TestOptions const& testOptions, @@ -857,6 +862,7 @@ TestResult runTest( { "COMPARE_HLSL", &runHLSLComparisonTest }, { "COMPARE_HLSL_RENDER", &runHLSLRenderComparisonTest }, { "COMPARE_HLSL_CROSS_COMPILE_RENDER", &runHLSLCrossCompileRenderComparisonTest}, + { "COMPARE_HLSL_GLSL_RENDER", &runHLSLAndGLSLComparisonTest }, { "COMPARE_GLSL", &runGLSLComparisonTest }, { nullptr, nullptr }, }; |
