diff options
| author | YONGH\yongh <yonghe@outlook.com> | 2017-10-19 18:18:21 -0400 |
|---|---|---|
| committer | YONGH\yongh <yonghe@outlook.com> | 2017-10-19 18:18:21 -0400 |
| commit | 8ff7412f988c77f21196b907820b94aa67eb6f21 (patch) | |
| tree | 27bea95dec68c42e5d3dd924da0cd8fa74f0d9e4 | |
| parent | 88023aea669f258d66e53eab10215337a7f72853 (diff) | |
Support running and comparing execution results of compute shaders in testing framework.
| -rw-r--r-- | source/core/text-io.h | 22 | ||||
| -rw-r--r-- | tests/compute/simple.slang | 11 | ||||
| -rw-r--r-- | tests/compute/simple.slang.actual.txt | 512 | ||||
| -rw-r--r-- | tests/compute/simple.slang.expected.txt | 4 | ||||
| -rw-r--r-- | tools/render-test/main.cpp | 140 | ||||
| -rw-r--r-- | tools/render-test/options.cpp | 8 | ||||
| -rw-r--r-- | tools/render-test/options.h | 7 | ||||
| -rw-r--r-- | tools/render-test/render-d3d11.cpp | 129 | ||||
| -rw-r--r-- | tools/render-test/render-gl.cpp | 68 | ||||
| -rw-r--r-- | tools/render-test/render.h | 16 | ||||
| -rw-r--r-- | tools/render-test/slang-support.cpp | 198 | ||||
| -rw-r--r-- | tools/slang-test/main.cpp | 53 |
12 files changed, 983 insertions, 185 deletions
diff --git a/source/core/text-io.h b/source/core/text-io.h index c914e340a..e4bdc6e2d 100644 --- a/source/core/text-io.h +++ b/source/core/text-io.h @@ -311,6 +311,28 @@ namespace Slang stream = 0; } }; + + inline List<String> Split(String text, char c) + { + List<String> result; + StringBuilder sb; + for (int i = 0; i < text.Length(); i++) + { + if (text[i] == c) + { + auto str = sb.ToString(); + if (str.Length() != 0) + result.Add(str); + sb.Clear(); + } + else + sb << text[i]; + } + auto lastStr = sb.ToString(); + if (lastStr.Length()) + result.Add(lastStr); + return result; + } } #endif diff --git a/tests/compute/simple.slang b/tests/compute/simple.slang new file mode 100644 index 000000000..3f43c8cc7 --- /dev/null +++ b/tests/compute/simple.slang @@ -0,0 +1,11 @@ +//TEST(smoke,compute):COMPARE_COMPUTE: + +// This is a basic test for Slang compute shader. + +RWStructuredBuffer<float> outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + outputBuffer[dispatchThreadID.x] = float(dispatchThreadID.x); +}
\ No newline at end of file diff --git a/tests/compute/simple.slang.actual.txt b/tests/compute/simple.slang.actual.txt new file mode 100644 index 000000000..e8a8ee20b --- /dev/null +++ b/tests/compute/simple.slang.actual.txt @@ -0,0 +1,512 @@ +0 +1 +2 +3 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 diff --git a/tests/compute/simple.slang.expected.txt b/tests/compute/simple.slang.expected.txt new file mode 100644 index 000000000..fdaa30664 --- /dev/null +++ b/tests/compute/simple.slang.expected.txt @@ -0,0 +1,4 @@ +0.0 +1.0 +2.0 +3.0
\ No newline at end of file diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp index f2688d275..6907ba40f 100644 --- a/tools/render-test/main.cpp +++ b/tools/render-test/main.cpp @@ -49,21 +49,24 @@ static const Vertex kVertexData[kVertexCount] = { // Global variables for state to be used for rendering... -uintptr_t gConstantBufferSize; +uintptr_t gConstantBufferSize, gComputeResultBufferSize; Buffer* gConstantBuffer; InputLayout* gInputLayout; Buffer* gVertexBuffer; +Buffer* gComputeResultBuffer; ShaderProgram* gShaderProgram; // Entry point name to use for vertex/fragment shader static char const* vertexEntryPointName = "vertexMain"; static char const* fragmentEntryPointName = "fragmentMain"; +static char const* computeEntryPointName = "computeMain"; // "Profile" to use when compiling for HLSL targets // TODO: does this belong here? static char const* vertexProfileName = "vs_4_0"; static char const* fragmentProfileName = "ps_4_0"; +static char const* computeProfileName = "cs_4_0"; Error initializeShaders( ShaderCompiler* shaderCompiler) @@ -95,13 +98,21 @@ Error initializeShaders( ShaderCompileRequest compileRequest; compileRequest.source = sourceInfo; - compileRequest.vertexShader.source = sourceInfo; - compileRequest.vertexShader.name = vertexEntryPointName; - compileRequest.vertexShader.profile = vertexProfileName; - compileRequest.fragmentShader.source = sourceInfo; - compileRequest.fragmentShader.name = fragmentEntryPointName; - compileRequest.fragmentShader.profile = fragmentProfileName; - + if (gOptions.shaderType == ShaderProgramType::Graphics) + { + compileRequest.vertexShader.source = sourceInfo; + compileRequest.vertexShader.name = vertexEntryPointName; + compileRequest.vertexShader.profile = vertexProfileName; + compileRequest.fragmentShader.source = sourceInfo; + compileRequest.fragmentShader.name = fragmentEntryPointName; + compileRequest.fragmentShader.profile = fragmentProfileName; + } + else + { + compileRequest.computeShader.source = sourceInfo; + compileRequest.computeShader.name = computeEntryPointName; + compileRequest.computeShader.profile = computeProfileName; + } gShaderProgram = shaderCompiler->compileProgram(compileRequest); if( !gShaderProgram ) { @@ -111,6 +122,15 @@ Error initializeShaders( return Error::None; } +void outputComputeResult(Renderer* renderer, const char * fileName) +{ + float* data = (float*)renderer->map(gComputeResultBuffer, MapFlavor::HostRead); + FILE* f = fopen(fileName, "wt"); + for (auto i = 0u; i < gComputeResultBufferSize / sizeof(UInt); i++) + fprintf(f, "%.9g\n", data[i]); + fclose(f); +} + // // At initialization time, we are going to load and compile our Slang shader // code, and then create the D3D11 API objects we need for rendering. @@ -138,6 +158,19 @@ Error initializeInner( if(!gConstantBuffer) return Error::Unexpected; + gComputeResultBufferSize = 512 * sizeof(float); + BufferDesc computeResultBufferDesc; + computeResultBufferDesc.size = gComputeResultBufferSize; + computeResultBufferDesc.flavor = BufferFlavor::Storage; + gComputeResultBuffer = renderer->createBuffer(computeResultBufferDesc); + if (!gComputeResultBufferSize) + return Error::Unexpected; + // initialize buffer to 0 + char * ptr = (char*)renderer->map(gComputeResultBuffer, MapFlavor::HostWrite); + for (auto i = 0u; i < gComputeResultBufferSize; i++) + ptr[i] = 0; + renderer->unmap(gComputeResultBuffer); + // Input Assembler (IA) InputElementDesc inputElements[] = { @@ -195,6 +228,13 @@ void renderFrameInner( renderer->draw(3); } +void runCompute(Renderer * renderer) +{ + renderer->setShaderProgram(gShaderProgram); + renderer->setStorageBuffer(0, gComputeResultBuffer); + renderer->dispatchCompute(1, 1, 1); +} + void finalize() { } @@ -352,44 +392,52 @@ int main( // Once initialization is all complete, we show the window... ShowWindow(windowHandle, showCommand); - - // ... and enter the event loop: - for(;;) - { - MSG message; - - int result = PeekMessageW(&message, NULL, 0, 0, PM_REMOVE); - if (result != 0) - { - if (message.message == WM_QUIT) - { - return (int)message.wParam; - } - - TranslateMessage(&message); - DispatchMessageW(&message); - } - else - { - // 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); - - // If we are in a mode where output is requested, we need to snapshot the back buffer here - if( gOptions.outputPath ) - { - renderer->captureScreenShot(gOptions.outputPath); - return 0; - } - - renderer->presentFrame(); - } - } + + // ... and enter the event loop: + for (;;) + { + MSG message; + + int result = PeekMessageW(&message, NULL, 0, 0, PM_REMOVE); + if (result != 0) + { + if (message.message == WM_QUIT) + { + return (int)message.wParam; + } + + TranslateMessage(&message); + DispatchMessageW(&message); + } + else + { + // 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(); + + if (gOptions.shaderType == ShaderProgramType::Compute) + { + runCompute(renderer); + } + else + { + renderFrameInner(renderer); + } + // If we are in a mode where output is requested, we need to snapshot the back buffer here + if (gOptions.outputPath) + { + if (gOptions.shaderType == ShaderProgramType::Compute) + outputComputeResult(renderer, gOptions.outputPath); + else + renderer->captureScreenShot(gOptions.outputPath); + return 0; + } + + renderer->presentFrame(); + } + } return 0; } diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index 53f88b7a9..89a68b27f 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -92,6 +92,14 @@ void parseOptions(int* argc, char** argv) } gOptions.slangArgs[gOptions.slangArgCount++] = *argCursor++; } + else if (strcmp(arg, "-compute") == 0) + { + gOptions.shaderType = ShaderProgramType::Compute; + } + else if (strcmp(arg, "-graphics") == 0) + { + gOptions.shaderType = ShaderProgramType::Graphics; + } else { fprintf(stderr, "unknown option '%s'\n", arg); diff --git a/tools/render-test/options.h b/tools/render-test/options.h index fbc615d56..a36e0a809 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -31,11 +31,18 @@ enum kMaxSlangArgs = 16, }; +enum class ShaderProgramType +{ + Graphics, + Compute +}; + struct Options { char const* appName = "render-test"; char const* sourcePath = nullptr; char const* outputPath = nullptr; + ShaderProgramType shaderType = ShaderProgramType::Graphics; Mode mode = Mode::Slang; char const* slangArgs[kMaxSlangArgs]; diff --git a/tools/render-test/render-d3d11.cpp b/tools/render-test/render-d3d11.cpp index bc86809e6..6e9e04a78 100644 --- a/tools/render-test/render-d3d11.cpp +++ b/tools/render-test/render-d3d11.cpp @@ -1,4 +1,4 @@ -// render-d3d11.cpp +// render-d3d11.cpp #include "render-d3d11.h" #include "options.h" @@ -418,6 +418,12 @@ public: return this; } + struct D3DBuffer + { + ID3D11UnorderedAccessView * view = nullptr; + ID3D11Buffer * buffer = nullptr; + }; + virtual Buffer* createBuffer(BufferDesc const& desc) override { D3D11_BUFFER_DESC dxBufferDesc = { 0 }; @@ -437,6 +443,14 @@ public: dxBufferDesc.CPUAccessFlags = 0; break; + case BufferFlavor::Storage: + dxBufferDesc.Usage = D3D11_USAGE_DEFAULT; + dxBufferDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS; + dxBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; + dxBufferDesc.StructureByteStride = sizeof(float); + dxBufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; + break; + default: return nullptr; } @@ -452,7 +466,20 @@ public: &dxBuffer); if(FAILED(hr)) return nullptr; - return (Buffer*) dxBuffer; + D3DBuffer * rs = new D3DBuffer(); + rs->buffer = dxBuffer; + if (desc.flavor == BufferFlavor::Storage) + { + D3D11_UNORDERED_ACCESS_VIEW_DESC viewDesc; + memset(&viewDesc, 0, sizeof(viewDesc)); + viewDesc.Buffer.FirstElement = 0; + viewDesc.Buffer.NumElements = 512; + viewDesc.Buffer.Flags = 0; + viewDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + viewDesc.Format = DXGI_FORMAT_UNKNOWN; + dxDevice->CreateUnorderedAccessView(dxBuffer, &viewDesc, &rs->view); + } + return (Buffer*) rs; } static DXGI_FORMAT mapFormat(Format format) @@ -535,7 +562,7 @@ public: { auto dxContext = dxImmediateContext; - auto dxBuffer = (ID3D11Buffer*) buffer; + auto dxBuffer = ((D3DBuffer*)buffer)->buffer; D3D11_MAP dxMapFlavor; switch( flavor ) @@ -543,7 +570,12 @@ public: case MapFlavor::WriteDiscard: dxMapFlavor = D3D11_MAP_WRITE_DISCARD; break; - + case MapFlavor::HostWrite: + dxMapFlavor = D3D11_MAP_WRITE; + break; + case MapFlavor::HostRead: + dxMapFlavor = D3D11_MAP_READ; + break; default: return nullptr; } @@ -563,7 +595,7 @@ public: { auto dxContext = dxImmediateContext; - auto dxBuffer = (ID3D11Buffer*) buffer; + auto dxBuffer = ((D3DBuffer*)buffer)->buffer; dxContext->Unmap(dxBuffer, 0); } @@ -609,11 +641,11 @@ public: dxVertexOffsets[ii] = (UINT) offsets[ii]; } - auto dxVertexBuffers = (ID3D11Buffer* const*) buffers; + auto dxVertexBuffers = (D3DBuffer* const*) buffers; dxContext->IASetVertexBuffers( (UINT) startSlot, - (UINT) slotCount, &dxVertexBuffers[0], &dxVertexStrides[0], &dxVertexOffsets[0]); + (UINT) slotCount, &(dxVertexBuffers[0])->buffer, &dxVertexStrides[0], &dxVertexOffsets[0]); } virtual void setShaderProgram(ShaderProgram* inProgram) override @@ -621,7 +653,7 @@ public: auto dxContext = dxImmediateContext; auto program = (D3D11ShaderProgram*) inProgram; - + dxContext->CSSetShader(program->dxComputeShader, NULL, 0); dxContext->VSSetShader(program->dxVertexShader, NULL, 0); dxContext->PSSetShader(program->dxPixelShader, NULL, 0); } @@ -632,14 +664,24 @@ public: // TODO: actually use those offsets - auto dxConstantBuffers = (ID3D11Buffer* const*) buffers; - + auto dxConstantBuffers = (D3DBuffer* const*) buffers; dxContext->VSSetConstantBuffers( - (UINT) startSlot, (UINT) slotCount, &dxConstantBuffers[0]); + (UINT) startSlot, (UINT) slotCount, &dxConstantBuffers[0]->buffer); dxContext->VSSetConstantBuffers( - (UINT) startSlot, (UINT) slotCount, &dxConstantBuffers[0]); + (UINT) startSlot, (UINT) slotCount, &dxConstantBuffers[0]->buffer); } + virtual void setStorageBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override + { + auto dxContext = dxImmediateContext; + + // TODO: actually use those offsets + + auto dxStorageBuffers = (D3DBuffer* const*)buffers; + dxContext->CSSetUnorderedAccessViews( + (UINT)startSlot, (UINT)slotCount, &dxStorageBuffers[0]->view, 0); + } + virtual void draw(UInt vertexCount, UInt startVertex) override { @@ -653,35 +695,62 @@ public: struct D3D11ShaderProgram { - ID3D11VertexShader* dxVertexShader; - ID3D11PixelShader* dxPixelShader; + ID3D11VertexShader* dxVertexShader = nullptr; + ID3D11PixelShader* dxPixelShader = nullptr; + ID3D11ComputeShader* dxComputeShader = nullptr; }; virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override { - auto dxVertexShaderBlob = compileHLSLShader(request.vertexShader.source.path, request.vertexShader.source.text, request.vertexShader .name, request.vertexShader .profile); - if(!dxVertexShaderBlob) return nullptr; + if (request.computeShader.name) + { + auto dxComputeShaderBlob = compileHLSLShader(request.computeShader.source.path, request.computeShader.source.text, request.computeShader.name, request.computeShader.profile); + if (!dxComputeShaderBlob) return nullptr; + + ID3D11ComputeShader* dxComputeShader; + + HRESULT csResult = dxDevice->CreateComputeShader(dxComputeShaderBlob->GetBufferPointer(), dxComputeShaderBlob->GetBufferSize(), nullptr, &dxComputeShader); + + dxComputeShaderBlob->Release(); - auto dxFragmentShaderBlob = compileHLSLShader(request.fragmentShader.source.path, request.fragmentShader.source.text, request.fragmentShader .name, request.fragmentShader .profile); - if(!dxFragmentShaderBlob) return nullptr; + if (FAILED(csResult)) return nullptr; - ID3D11VertexShader* dxVertexShader; - ID3D11PixelShader* dxPixelShader; + D3D11ShaderProgram* shaderProgram = new D3D11ShaderProgram(); + shaderProgram->dxComputeShader = dxComputeShader; + return (ShaderProgram*)shaderProgram; + } + else + { + auto dxVertexShaderBlob = compileHLSLShader(request.vertexShader.source.path, request.vertexShader.source.text, request.vertexShader.name, request.vertexShader.profile); + if (!dxVertexShaderBlob) return nullptr; - HRESULT vsResult = dxDevice->CreateVertexShader( dxVertexShaderBlob ->GetBufferPointer(), dxVertexShaderBlob ->GetBufferSize(), nullptr, &dxVertexShader); - HRESULT psResult = dxDevice->CreatePixelShader( dxFragmentShaderBlob->GetBufferPointer(), dxFragmentShaderBlob->GetBufferSize(), nullptr, &dxPixelShader); + auto dxFragmentShaderBlob = compileHLSLShader(request.fragmentShader.source.path, request.fragmentShader.source.text, request.fragmentShader.name, request.fragmentShader.profile); + if (!dxFragmentShaderBlob) return nullptr; - dxVertexShaderBlob ->Release(); - dxFragmentShaderBlob->Release(); + ID3D11VertexShader* dxVertexShader; + ID3D11PixelShader* dxPixelShader; - if(FAILED(vsResult)) return nullptr; - if(FAILED(psResult)) return nullptr; + HRESULT vsResult = dxDevice->CreateVertexShader(dxVertexShaderBlob->GetBufferPointer(), dxVertexShaderBlob->GetBufferSize(), nullptr, &dxVertexShader); + HRESULT psResult = dxDevice->CreatePixelShader(dxFragmentShaderBlob->GetBufferPointer(), dxFragmentShaderBlob->GetBufferSize(), nullptr, &dxPixelShader); - D3D11ShaderProgram* shaderProgram = new D3D11ShaderProgram(); - shaderProgram->dxVertexShader = dxVertexShader; - shaderProgram->dxPixelShader = dxPixelShader; - return (ShaderProgram*) shaderProgram; + dxVertexShaderBlob->Release(); + dxFragmentShaderBlob->Release(); + + if (FAILED(vsResult)) return nullptr; + if (FAILED(psResult)) return nullptr; + + D3D11ShaderProgram* shaderProgram = new D3D11ShaderProgram(); + shaderProgram->dxVertexShader = dxVertexShader; + shaderProgram->dxPixelShader = dxPixelShader; + return (ShaderProgram*)shaderProgram; + } } + + virtual void dispatchCompute(int x, int y, int z) override + { + auto dxContext = dxImmediateContext; + dxContext->Dispatch(x, y, z); + } }; diff --git a/tools/render-test/render-gl.cpp b/tools/render-test/render-gl.cpp index f02ac36c4..55647fb25 100644 --- a/tools/render-test/render-gl.cpp +++ b/tools/render-test/render-gl.cpp @@ -58,6 +58,7 @@ F(glEnableVertexAttribArray, PFNGLENABLEVERTEXATTRIBARRAYPROC) \ F(glDisableVertexAttribArray, PFNGLDISABLEVERTEXATTRIBARRAYPROC) \ F(glDebugMessageCallback, PFNGLDEBUGMESSAGECALLBACKPROC) \ + F(glDispatchCompute, PFNGLDISPATCHCOMPUTEPROC) \ /* end */ namespace renderer_test { @@ -311,8 +312,12 @@ public: switch (flavor) { case MapFlavor::WriteDiscard: + case MapFlavor::HostWrite: access = GL_WRITE_ONLY; break; + case MapFlavor::HostRead: + access = GL_READ_ONLY; + break; } auto bufferID = (GLuint)(uintptr_t)buffer; @@ -377,21 +382,32 @@ public: 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; + void bindBufferImpl(int target, UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) + { + for (UInt ii = 0; ii < slotCount; ++ii) + { + UInt slot = startSlot + ii; - Buffer* buffer = buffers[ii]; - GLuint bufferID = (GLuint)(uintptr_t)buffer; + Buffer* buffer = buffers[ii]; + GLuint bufferID = (GLuint)(uintptr_t)buffer; - assert(!offsets || !offsets[ii]); + assert(!offsets || !offsets[ii]); - glBindBufferBase(GL_UNIFORM_BUFFER, (GLuint) slot, bufferID); - } + glBindBufferBase(target, (GLuint)slot, bufferID); + } + } + + virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override + { + bindBufferImpl(GL_UNIFORM_BUFFER, startSlot, slotCount, buffers, offsets); } + + virtual void setStorageBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override + { + bindBufferImpl(GL_SHADER_STORAGE_BUFFER, startSlot, slotCount, buffers, offsets); + } + void flushStateForDraw() { auto layout = this->boundInputLayout; @@ -432,18 +448,31 @@ public: virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override { auto programID = glCreateProgram(); + if (request.computeShader.name) + { + auto computeShaderID = loadShader(GL_COMPUTE_SHADER, request.computeShader.source.text); - auto vertexShaderID = loadShader(GL_VERTEX_SHADER, request.vertexShader .source.text); - auto fragmentShaderID = loadShader(GL_FRAGMENT_SHADER, request.fragmentShader.source.text); + glAttachShader(programID, computeShaderID); - glAttachShader(programID, vertexShaderID); - glAttachShader(programID, fragmentShaderID); - glLinkProgram(programID); + glLinkProgram(programID); - glDeleteShader(vertexShaderID); - glDeleteShader(fragmentShaderID); + glDeleteShader(computeShaderID); + } + else + { + auto vertexShaderID = loadShader(GL_VERTEX_SHADER, request.vertexShader.source.text); + auto fragmentShaderID = loadShader(GL_FRAGMENT_SHADER, request.fragmentShader.source.text); + glAttachShader(programID, vertexShaderID); + glAttachShader(programID, fragmentShaderID); + + + glLinkProgram(programID); + + glDeleteShader(vertexShaderID); + glDeleteShader(fragmentShaderID); + } GLint success = GL_FALSE; glGetProgramiv(programID, GL_LINK_STATUS, &success); if( !success ) @@ -579,6 +608,11 @@ public: return shaderID; } + + virtual void dispatchCompute(int x, int y, int z) override + { + glDispatchCompute(x, y, z); + } }; diff --git a/tools/render-test/render.h b/tools/render-test/render.h index afa279c66..7f8f2fffa 100644 --- a/tools/render-test/render.h +++ b/tools/render-test/render.h @@ -20,8 +20,8 @@ struct ShaderCompileRequest struct EntryPoint { - char const* name; - char const* profile; + char const* name = nullptr; + char const* profile = nullptr; SourceInfo source; }; @@ -29,6 +29,7 @@ struct ShaderCompileRequest SourceInfo source; EntryPoint vertexShader; EntryPoint fragmentShader; + EntryPoint computeShader; }; class ShaderCompiler @@ -47,6 +48,7 @@ enum class BufferFlavor { Constant, Vertex, + Storage, }; struct BufferDesc @@ -66,6 +68,8 @@ struct InputElementDesc enum class MapFlavor { + HostRead, + HostWrite, WriteDiscard, }; @@ -108,13 +112,17 @@ public: virtual void setShaderProgram(ShaderProgram* program) = 0; virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) = 0; - + virtual void setStorageBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) = 0; inline void setConstantBuffer(UInt slot, Buffer* buffer, UInt offset = 0) { setConstantBuffers(slot, 1, &buffer, &offset); } - + inline void setStorageBuffer(UInt slot, Buffer* buffer, UInt offset = 0) + { + setStorageBuffers(slot, 1, &buffer, &offset); + } virtual void draw(UInt vertexCount, UInt startVertex = 0) = 0; + virtual void dispatchCompute(int x, int y, int z) = 0; }; } // renderer_test diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp index bf8b7b9c7..63e24126c 100644 --- a/tools/render-test/slang-support.cpp +++ b/tools/render-test/slang-support.cpp @@ -13,94 +13,116 @@ struct SlangShaderCompilerWrapper : public ShaderCompiler SlangCompileTarget target; SlangSourceLanguage sourceLanguage; - virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override - { - SlangSession* slangSession = spCreateSession(NULL); - SlangCompileRequest* slangRequest = spCreateCompileRequest(slangSession); - - spSetCodeGenTarget(slangRequest, target); - - // Define a macro so that shader code in a test can detect what language we - // are nominally working with. - char const* langDefine = nullptr; - switch (sourceLanguage) - { - case SLANG_SOURCE_LANGUAGE_GLSL: langDefine = "__GLSL__"; break; - case SLANG_SOURCE_LANGUAGE_HLSL: langDefine = "__HLSL__"; break; - case SLANG_SOURCE_LANGUAGE_SLANG: langDefine = "__SLANG__"; break; - default: - assert(!"unexpected"); - break; - } - spAddPreprocessorDefine(slangRequest, langDefine, "1"); - - spProcessCommandLineArguments(slangRequest, &gOptions.slangArgs[0], gOptions.slangArgCount); - - int vertexTranslationUnit = 0; - int fragmentTranslationUnit = 0; - char const* vertexEntryPointName = request.vertexShader.name; - char const* fragmentEntryPointName = request.fragmentShader.name; - if( sourceLanguage == SLANG_SOURCE_LANGUAGE_GLSL ) - { - // GLSL presents unique challenges because, frankly, it got the whole - // compilation model wrong. One aspect of working around this is that - // we will compile the same source file multiple times: once per - // entry point, and we will have different preprocessor definitions - // active in each case. - - vertexTranslationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); - spAddTranslationUnitSourceString(slangRequest, vertexTranslationUnit, request.source.path, request.source.text); - spTranslationUnit_addPreprocessorDefine(slangRequest, vertexTranslationUnit, "__GLSL_VERTEX__", "1"); - vertexEntryPointName = "main"; - - fragmentTranslationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); - spAddTranslationUnitSourceString(slangRequest, fragmentTranslationUnit, request.source.path, request.source.text); - spTranslationUnit_addPreprocessorDefine(slangRequest, fragmentTranslationUnit, "__GLSL_FRAGMENT__", "1"); - fragmentEntryPointName = "main"; - } - else - { - int translationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); - spAddTranslationUnitSourceString(slangRequest, translationUnit, request.source.path, request.source.text); - - vertexTranslationUnit = translationUnit; - fragmentTranslationUnit = translationUnit; - } - - - // If we aren't dealing with true Slang input, then don't enable checking. - if (sourceLanguage != SLANG_SOURCE_LANGUAGE_SLANG) - { - spSetCompileFlags(slangRequest, SLANG_COMPILE_FLAG_NO_CHECKING); - } - - int vertexEntryPoint = spAddEntryPoint(slangRequest, vertexTranslationUnit, vertexEntryPointName, spFindProfile(slangSession, request.vertexShader.profile)); - int fragmentEntryPoint = spAddEntryPoint(slangRequest, fragmentTranslationUnit, fragmentEntryPointName, spFindProfile(slangSession, request.fragmentShader.profile)); - - int compileErr = spCompile(slangRequest); - if(auto diagnostics = spGetDiagnosticOutput(slangRequest)) - { - // TODO(tfoley): re-enable when I get a logging solution in place -// OutputDebugStringA(diagnostics); - fprintf(stderr, "%s", diagnostics); - } - if(compileErr) - { - return nullptr; - } - - - ShaderCompileRequest innerRequest = request; - - char const* vertexCode = spGetEntryPointSource(slangRequest, vertexEntryPoint); - char const* fragmentCode = spGetEntryPointSource(slangRequest, fragmentEntryPoint); - - innerRequest.vertexShader.source.text = vertexCode; - innerRequest.fragmentShader.source.text = fragmentCode; - - - auto result = innerCompiler->compileProgram(innerRequest); - + virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override + { + SlangSession* slangSession = spCreateSession(NULL); + SlangCompileRequest* slangRequest = spCreateCompileRequest(slangSession); + + spSetCodeGenTarget(slangRequest, target); + + // Define a macro so that shader code in a test can detect what language we + // are nominally working with. + char const* langDefine = nullptr; + switch (sourceLanguage) + { + case SLANG_SOURCE_LANGUAGE_GLSL: langDefine = "__GLSL__"; break; + case SLANG_SOURCE_LANGUAGE_HLSL: langDefine = "__HLSL__"; break; + case SLANG_SOURCE_LANGUAGE_SLANG: langDefine = "__SLANG__"; break; + default: + assert(!"unexpected"); + break; + } + spAddPreprocessorDefine(slangRequest, langDefine, "1"); + + spProcessCommandLineArguments(slangRequest, &gOptions.slangArgs[0], gOptions.slangArgCount); + int computeTranslationUnit = 0; + int vertexTranslationUnit = 0; + int fragmentTranslationUnit = 0; + char const* vertexEntryPointName = request.vertexShader.name; + char const* fragmentEntryPointName = request.fragmentShader.name; + char const* computeEntryPointName = request.computeShader.name; + + if (sourceLanguage == SLANG_SOURCE_LANGUAGE_GLSL) + { + // GLSL presents unique challenges because, frankly, it got the whole + // compilation model wrong. One aspect of working around this is that + // we will compile the same source file multiple times: once per + // entry point, and we will have different preprocessor definitions + // active in each case. + + vertexTranslationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); + spAddTranslationUnitSourceString(slangRequest, vertexTranslationUnit, request.source.path, request.source.text); + spTranslationUnit_addPreprocessorDefine(slangRequest, vertexTranslationUnit, "__GLSL_VERTEX__", "1"); + vertexEntryPointName = "main"; + + fragmentTranslationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); + spAddTranslationUnitSourceString(slangRequest, fragmentTranslationUnit, request.source.path, request.source.text); + spTranslationUnit_addPreprocessorDefine(slangRequest, fragmentTranslationUnit, "__GLSL_FRAGMENT__", "1"); + fragmentEntryPointName = "main"; + + computeTranslationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); + spAddTranslationUnitSourceString(slangRequest, computeTranslationUnit, request.source.path, request.source.text); + spTranslationUnit_addPreprocessorDefine(slangRequest, computeTranslationUnit, "__GLSL_COMPUTE__", "1"); + computeEntryPointName = "main"; + } + else + { + int translationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); + spAddTranslationUnitSourceString(slangRequest, translationUnit, request.source.path, request.source.text); + + vertexTranslationUnit = translationUnit; + fragmentTranslationUnit = translationUnit; + computeTranslationUnit = translationUnit; + } + + + // If we aren't dealing with true Slang input, then don't enable checking. + if (sourceLanguage != SLANG_SOURCE_LANGUAGE_SLANG) + { + spSetCompileFlags(slangRequest, SLANG_COMPILE_FLAG_NO_CHECKING); + } + ShaderProgram * result = nullptr; + if (request.computeShader.name) + { + int computeEntryPoint = spAddEntryPoint(slangRequest, computeTranslationUnit, computeEntryPointName, spFindProfile(slangSession, request.computeShader.profile)); + int compileErr = spCompile(slangRequest); + if (auto diagnostics = spGetDiagnosticOutput(slangRequest)) + { + fprintf(stderr, "%s", diagnostics); + } + if (!compileErr) + { + ShaderCompileRequest innerRequest = request; + char const* computeCode = spGetEntryPointSource(slangRequest, computeEntryPoint); + innerRequest.computeShader.source.text = computeCode; + result = innerCompiler->compileProgram(innerRequest); + } + } + else + { + int vertexEntryPoint = spAddEntryPoint(slangRequest, vertexTranslationUnit, vertexEntryPointName, spFindProfile(slangSession, request.vertexShader.profile)); + int fragmentEntryPoint = spAddEntryPoint(slangRequest, fragmentTranslationUnit, fragmentEntryPointName, spFindProfile(slangSession, request.fragmentShader.profile)); + + int compileErr = spCompile(slangRequest); + if (auto diagnostics = spGetDiagnosticOutput(slangRequest)) + { + // TODO(tfoley): re-enable when I get a logging solution in place + // OutputDebugStringA(diagnostics); + fprintf(stderr, "%s", diagnostics); + } + if (!compileErr) + { + ShaderCompileRequest innerRequest = request; + + char const* vertexCode = spGetEntryPointSource(slangRequest, vertexEntryPoint); + char const* fragmentCode = spGetEntryPointSource(slangRequest, fragmentEntryPoint); + + innerRequest.vertexShader.source.text = vertexCode; + innerRequest.fragmentShader.source.text = fragmentCode; + + result = innerCompiler->compileProgram(innerRequest); + } + } // We clean up the Slang compilation context and result *after* // we have run the downstream compiler, because Slang // owns the memory allocation for the generated text, and will diff --git a/tools/slang-test/main.cpp b/tools/slang-test/main.cpp index 5c0c81392..fb10d46f6 100644 --- a/tools/slang-test/main.cpp +++ b/tools/slang-test/main.cpp @@ -1110,7 +1110,55 @@ TestResult runGLSLComparisonTest(TestInput& input) return kTestResult_Pass; } +TestResult doComputeComparisonTestRunImpl(TestInput& input, const char * langOption, String referenceOutput) +{ + // TODO: delete any existing files at the output path(s) to avoid stale outputs leading to a false pass + auto filePath999 = input.filePath; + auto outputStem = input.outputStem; + + OSProcessSpawner spawner; + + spawner.pushExecutablePath(String(options.binDir) + "render-test" + osGetExecutableSuffix()); + spawner.pushArgument(filePath999); + + for (auto arg : input.testOptions->args) + { + spawner.pushArgument(arg); + } + + spawner.pushArgument(langOption); + spawner.pushArgument("-o"); + spawner.pushArgument(outputStem + ".actual.txt"); + + if (spawnAndWait(outputStem, spawner) != kOSError_None) + { + return kTestResult_Fail; + } + + // check against reference output + if (!File::Exists(outputStem + ".actual.txt")) + return kTestResult_Fail; + if (!File::Exists(referenceOutput)) + return kTestResult_Fail; + + auto actualProgramOutput = Split(File::ReadAllText(outputStem + ".actual.txt"), '\n'); + auto referenceProgramOutput = Split(File::ReadAllText(referenceOutput), '\n'); + if (actualProgramOutput.Count() < referenceProgramOutput.Count()) + return kTestResult_Fail; + for (int i = 0; i < referenceProgramOutput.Count(); i++) + { + auto reference = StringToFloat(referenceProgramOutput[i]); + auto actual = StringToFloat(actualProgramOutput[i]); + if (abs(actual - reference) > 1e-7f) + return kTestResult_Fail; + } + return kTestResult_Pass; +} +TestResult doSlangComputeComparisonTest(TestInput& input) +{ + return doComputeComparisonTestRunImpl(input, "-slang -compute", input.outputStem + ".expected.txt"); +} TestResult doRenderComparisonTestRun(TestInput& input, char const* langOption, char const* outputKind, String* outOutput) { @@ -1317,6 +1365,8 @@ TestResult runTest( { "COMPARE_HLSL_GLSL_RENDER", &skipTest }, #endif { "COMPARE_GLSL", &runGLSLComparisonTest }, + { "COMPARE_COMPUTE", &doSlangComputeComparisonTest}, + { "CROSS_COMPILE", &runCrossCompilerTest }, { "EVAL", &runEvalTest }, { nullptr, nullptr }, @@ -1621,6 +1671,9 @@ int main( auto renderTestCategory = addTestCategory("render", fullTestCategory); + auto computeTestCategory = addTestCategory("compute", fullTestCategory); + + // An un-categorized test will always belong to the `full` category defaultTestCategory = fullTestCategory; |
