From f8472940229e8582ec9f941069fc9576bd09b94c Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Thu, 3 May 2018 18:42:13 -0400 Subject: Added Surface type - as a simple value type to hold a 2d collection of pixels. (#548) Added PngSerializeUtil allows currently for just writing Surface of RGBA format. Removes dependency on stbi_image except for in PngSerializeUtil. Removed use of gWindowWidth/Height globals - pass the height into initialize or Renderer. --- tools/render-test/main.cpp | 13 +- tools/render-test/options.h | 4 - tools/render-test/png-serialize-util.cpp | 38 +++++ tools/render-test/png-serialize-util.h | 14 ++ tools/render-test/render-d3d11.cpp | 39 +++--- tools/render-test/render-d3d12.cpp | 66 ++++----- tools/render-test/render-gl.cpp | 59 ++------ tools/render-test/render-test.vcxproj | 4 + tools/render-test/render-test.vcxproj.filters | 12 ++ tools/render-test/render-vk.cpp | 8 +- tools/render-test/render.h | 7 +- tools/render-test/surface.cpp | 191 ++++++++++++++++++++++++++ tools/render-test/surface.h | 83 +++++++++++ 13 files changed, 423 insertions(+), 115 deletions(-) create mode 100644 tools/render-test/png-serialize-util.cpp create mode 100644 tools/render-test/png-serialize-util.h create mode 100644 tools/render-test/surface.cpp create mode 100644 tools/render-test/surface.h (limited to 'tools') diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp index 7dcb9af95..df9220b58 100644 --- a/tools/render-test/main.cpp +++ b/tools/render-test/main.cpp @@ -430,7 +430,11 @@ SlangResult innerMain(int argc, char** argv) return SLANG_FAIL; } - SLANG_RETURN_ON_FAIL(renderer->initialize(windowHandle)); + Renderer::Desc desc; + desc.width = gWindowWidth; + desc.height = gWindowHeight; + + SLANG_RETURN_ON_FAIL(renderer->initialize(desc, windowHandle)); auto shaderCompiler = renderer->getShaderCompiler(); switch (gOptions.inputLanguageID) @@ -500,7 +504,12 @@ SlangResult innerMain(int argc, char** argv) } else { - SLANG_RETURN_ON_FAIL(renderer->captureScreenShot(gOptions.outputPath)); + Result res = renderer->captureScreenShot(gOptions.outputPath); + if (SLANG_FAILED(res)) + { + fprintf(stderr, "ERROR: failed to write screen capture to file\n"); + return res; + } } return SLANG_OK; } diff --git a/tools/render-test/options.h b/tools/render-test/options.h index 56bd638ef..630c30d8a 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -51,10 +51,6 @@ struct Options extern Options gOptions; -extern int gWindowWidth; -extern int gWindowHeight; - - SlangResult parseOptions(int* argc, char** argv); } // renderer_test diff --git a/tools/render-test/png-serialize-util.cpp b/tools/render-test/png-serialize-util.cpp new file mode 100644 index 000000000..a7f6aa83a --- /dev/null +++ b/tools/render-test/png-serialize-util.cpp @@ -0,0 +1,38 @@ +// png-serialize-util.cpp +#define _CRT_SECURE_NO_WARNINGS + +#include "png-serialize-util.h" + +#include +#include + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "external/stb/stb_image_write.h" + +namespace renderer_test { +using namespace Slang; + +/* static */Slang::Result PngSerializeUtil::write(const char* filename, const Surface& surface) +{ + int numComps = 0; + switch (surface.m_format) + { + case Format::RGBA_Unorm_UInt8: + { + numComps = 4; + break; + } + default: break; + } + + if (numComps <= 0) + { + return SLANG_FAIL; + } + + int stbResult = stbi_write_png(filename, surface.m_width, surface.m_height, numComps, surface.m_data, surface.m_rowStrideInBytes); + + return stbResult ? SLANG_OK : SLANG_FAIL; +} + +} // renderer_test diff --git a/tools/render-test/png-serialize-util.h b/tools/render-test/png-serialize-util.h new file mode 100644 index 000000000..fe3b4f873 --- /dev/null +++ b/tools/render-test/png-serialize-util.h @@ -0,0 +1,14 @@ +// png-serialize-util.h +#pragma once + +#include "surface.h" + +namespace renderer_test { + +struct PngSerializeUtil +{ + static Slang::Result write(const char* filename, const Surface& surface); + +}; + +} // renderer_test diff --git a/tools/render-test/render-d3d11.cpp b/tools/render-test/render-d3d11.cpp index e2bcf1659..96835f313 100644 --- a/tools/render-test/render-d3d11.cpp +++ b/tools/render-test/render-d3d11.cpp @@ -1,4 +1,7 @@ // render-d3d11.cpp + +#define _CRT_SECURE_NO_WARNINGS + #include "render-d3d11.h" #include "options.h" @@ -11,11 +14,7 @@ #include "../../source/core/slang-com-ptr.h" -#ifdef _MSC_VER -#pragma warning(disable: 4996) -#endif -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "external/stb/stb_image_write.h" +#include "png-serialize-util.h" // We will be rendering with Direct3D 11, so we need to include // the Windows and D3D11 headers @@ -47,7 +46,7 @@ class D3D11Renderer : public Renderer, public ShaderCompiler { public: // Renderer implementation - virtual SlangResult initialize(void* inWindowHandle) override; + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; virtual void setClearColor(const float color[4]) override; virtual void clearFrame() override; virtual void presentFrame() override; @@ -156,6 +155,8 @@ public: RefPtr m_currentBindings; + Desc m_desc; + float m_clearColor[4] = { 0, 0, 0, 0 }; }; @@ -215,26 +216,22 @@ Renderer* createD3D11Renderer() return hr; } - int stbResult = stbi_write_png(outputPath, textureDesc.Width, textureDesc.Height, 4, - mappedResource.pData, mappedResource.RowPitch); + Surface surface; + surface.setUnowned(textureDesc.Width, textureDesc.Height, Format::RGBA_Unorm_UInt8, mappedResource.RowPitch, mappedResource.pData); + + Result res = PngSerializeUtil::write(outputPath, surface); // Make sure to unmap context->Unmap(stagingTexture, 0); - - if (!stbResult) - { - fprintf(stderr, "ERROR: failed to write texture to file\n"); - return E_UNEXPECTED; - } - - return S_OK; + return res; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! -SlangResult D3D11Renderer::initialize(void* inWindowHandle) +SlangResult D3D11Renderer::initialize(const Desc& desc, void* inWindowHandle) { auto windowHandle = (HWND)inWindowHandle; + m_desc = desc; // Rather than statically link against D3D, we load it dynamically. HMODULE d3dModule = LoadLibraryA("d3d11.dll"); @@ -287,8 +284,8 @@ SlangResult D3D11Renderer::initialize(void* inWindowHandle) D3D_FEATURE_LEVEL_9_1, }; D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_9_1; - const int totalNumFeatureLevels = sizeof(featureLevels) / sizeof(featureLevels[0]); - + const int totalNumFeatureLevels = SLANG_COUNT_OF(featureLevels); + // On a machine that does not have an up-to-date version of D3D installed, // the `D3D11CreateDeviceAndSwapChain` call will fail with `E_INVALIDARG` // if you ask for featuer level 11_1. The workaround is to call @@ -360,8 +357,8 @@ SlangResult D3D11Renderer::initialize(void* inWindowHandle) D3D11_VIEWPORT viewport; viewport.TopLeftX = 0; viewport.TopLeftY = 0; - viewport.Width = (float)gWindowWidth; - viewport.Height = (float)gWindowHeight; + viewport.Width = (float)desc.width; + viewport.Height = (float)desc.height; viewport.MaxDepth = 1; // TODO(tfoley): use reversed depth viewport.MinDepth = 0; m_immediateContext->RSSetViewports(1, &viewport); diff --git a/tools/render-test/render-d3d12.cpp b/tools/render-test/render-d3d12.cpp index 635c6739f..d1b547f2d 100644 --- a/tools/render-test/render-d3d12.cpp +++ b/tools/render-test/render-d3d12.cpp @@ -1,4 +1,6 @@ // render-d3d12.cpp +#define _CRT_SECURE_NO_WARNINGS + #include "render-d3d12.h" #include "options.h" @@ -23,19 +25,14 @@ #include "../../source/core/slang-com-ptr.h" +#include "png-serialize-util.h" + #include "resource-d3d12.h" #include "descriptor-heap-d3d12.h" #include "circular-resource-heap-d3d12.h" #include "d3d-util.h" -#ifdef _MSC_VER -#pragma warning(disable: 4996) -#endif - -//#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "external/stb/stb_image_write.h" - // We will use the C standard library just for printing error messages. #include @@ -56,7 +53,7 @@ class D3D12Renderer : public Renderer, public ShaderCompiler { public: // Renderer implementation - virtual SlangResult initialize(void* inWindowHandle) override; + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; virtual void setClearColor(const float color[4]) override; virtual void clearFrame() override; virtual void presentFrame() override; @@ -367,9 +364,8 @@ protected: int m_numTargetSamples = 1; ///< The number of multi sample samples int m_targetSampleQuality = 0; ///< The multi sample quality - int m_windowWidth = 0; - int m_windowHeight = 0; - + Desc m_desc; + bool m_isInitialized = false; D3D12_PRIMITIVE_TOPOLOGY_TYPE m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; @@ -790,7 +786,6 @@ Result D3D12Renderer::captureTextureToFile(D3D12Resource& resource, const char* // Submit the copy, and wait for copy to complete submitGpuWorkAndWait(); - int stbResult = 0; { ID3D12Resource* dxResource = stagingResource; @@ -799,17 +794,13 @@ Result D3D12Renderer::captureTextureToFile(D3D12Resource& resource, const char* SLANG_RETURN_ON_FAIL(dxResource->Map(0, &readRange, reinterpret_cast(&data))); - stbResult = stbi_write_png(outputPath, int(desc.Width), int(desc.Height), 4, data, int(rowPitch)); - - dxResource->Unmap(0, nullptr); - } + Surface surface; + surface.setUnowned(int(desc.Width), int(desc.Height), Format::RGBA_Unorm_UInt8, int(rowPitch), data); + Result res = PngSerializeUtil::write(outputPath, surface); - if (!stbResult) - { - fprintf(stderr, "ERROR: failed to write texture to file\n"); - return SLANG_FAIL; + dxResource->Unmap(0, nullptr); + return res; } - return SLANG_OK; } Result D3D12Renderer::calcComputePipelineState(ComPtr& signatureOut, ComPtr& pipelineStateOut) @@ -1259,7 +1250,7 @@ Result D3D12Renderer::_bindRenderState(RenderState* renderState, ID3D12GraphicsC // !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! -Result D3D12Renderer::initialize(void* inWindowHandle) +Result D3D12Renderer::initialize(const Desc& desc, void* inWindowHandle) { m_hwnd = (HWND)inWindowHandle; // Rather than statically link against D3D, we load it dynamically. @@ -1372,13 +1363,12 @@ Result D3D12Renderer::initialize(void* inWindowHandle) m_numRenderFrames = 3; m_numRenderTargets = 2; - m_windowWidth = gWindowWidth; - m_windowHeight = gWindowHeight; - + m_desc = desc; + // set viewport { - m_viewport.Width = float(m_windowWidth); - m_viewport.Height = float(m_windowHeight); + m_viewport.Width = float(m_desc.width); + m_viewport.Height = float(m_desc.height); m_viewport.MinDepth = 0; m_viewport.MaxDepth = 1; m_viewport.TopLeftX = 0; @@ -1388,8 +1378,8 @@ Result D3D12Renderer::initialize(void* inWindowHandle) { m_scissorRect.left = 0; m_scissorRect.top = 0; - m_scissorRect.right = m_windowWidth; - m_scissorRect.bottom = m_windowHeight; + m_scissorRect.right = m_desc.width; + m_scissorRect.bottom = m_desc.height; } // Describe and create the command queue. @@ -1402,8 +1392,8 @@ Result D3D12Renderer::initialize(void* inWindowHandle) // Describe the swap chain. DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; swapChainDesc.BufferCount = m_numRenderTargets; - swapChainDesc.BufferDesc.Width = m_windowWidth; - swapChainDesc.BufferDesc.Height = m_windowHeight; + swapChainDesc.BufferDesc.Width = m_desc.width; + swapChainDesc.BufferDesc.Height = m_desc.height; swapChainDesc.BufferDesc.Format = m_targetFormat; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; @@ -1571,7 +1561,7 @@ Result D3D12Renderer::createFrameResources() { D3D12_RESOURCE_DESC desc = m_backBuffers[0]->getResource()->GetDesc(); - assert(desc.Width == UINT64(m_windowWidth) && desc.Height == UINT64(m_windowHeight)); + assert(desc.Width == UINT64(m_desc.width) && desc.Height == UINT64(m_desc.height)); } // Create the depth stencil view. @@ -1598,8 +1588,8 @@ Result D3D12Renderer::createFrameResources() D3D12_RESOURCE_DESC resourceDesc = {}; resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; resourceDesc.Format = resourceFormat; - resourceDesc.Width = m_windowWidth; - resourceDesc.Height = m_windowHeight; + resourceDesc.Width = m_desc.width; + resourceDesc.Height = m_desc.height; resourceDesc.DepthOrArraySize = 1; resourceDesc.MipLevels = 1; resourceDesc.SampleDesc.Count = m_numTargetSamples; @@ -1621,12 +1611,12 @@ Result D3D12Renderer::createFrameResources() m_depthStencilView = m_dsvHeap->GetCPUDescriptorHandleForHeapStart(); } - m_viewport.Width = static_cast(m_windowWidth); - m_viewport.Height = static_cast(m_windowHeight); + m_viewport.Width = static_cast(m_desc.width); + m_viewport.Height = static_cast(m_desc.height); m_viewport.MaxDepth = 1.0f; - m_scissorRect.right = static_cast(m_windowWidth); - m_scissorRect.bottom = static_cast(m_windowHeight); + m_scissorRect.right = static_cast(m_desc.width); + m_scissorRect.bottom = static_cast(m_desc.height); return SLANG_OK; } diff --git a/tools/render-test/render-gl.cpp b/tools/render-test/render-gl.cpp index b079d3f10..cef482798 100644 --- a/tools/render-test/render-gl.cpp +++ b/tools/render-test/render-gl.cpp @@ -11,6 +11,9 @@ #include "core/secure-crt.h" #include "external/stb/stb_image_write.h" +#include "surface.h" +#include "png-serialize-util.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... @@ -78,7 +81,7 @@ class GLRenderer : public Renderer, public ShaderCompiler public: // Renderer implementation - virtual SlangResult initialize(void* inWindowHandle) override; + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; virtual void setClearColor(const float color[4]) override; virtual void clearFrame() override; virtual void presentFrame() override; @@ -276,6 +279,8 @@ public: UInt m_boundVertexStreamStrides[kMaxVertexStreams]; UInt m_boundVertexStreamOffsets[kMaxVertexStreams]; + Desc m_desc; + // Declare a function pointer for each OpenGL // extension function we need to load #define DECLARE_GL_EXTENSION_FUNC(NAME, TYPE) TYPE NAME; @@ -520,9 +525,10 @@ void GLRenderer::destroyBindingEntries(const BindingState::Desc& desc, const Bin // !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! -SlangResult GLRenderer::initialize(void* inWindowHandle) +SlangResult GLRenderer::initialize(const Desc& desc, void* inWindowHandle) { auto windowHandle = (HWND)inWindowHandle; + m_desc = desc; m_hdc = ::GetDC(windowHandle); @@ -553,7 +559,7 @@ SlangResult GLRenderer::initialize(void* inWindowHandle) glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); - glViewport(0, 0, gWindowWidth, gWindowHeight); + glViewport(0, 0, desc.width, desc.height); if (glDebugMessageCallback) { @@ -582,48 +588,11 @@ void GLRenderer::presentFrame() SlangResult GLRenderer::captureScreenShot(const char* outputPath) { - 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); - - ::free(buffer); - - if (!stbResult) - { - assert(!"unexpected"); - return SLANG_FAIL; - } - - return SLANG_OK; + Surface surface; + surface.allocate(m_desc.width, m_desc.height, Format::RGBA_Unorm_UInt8, 1, SurfaceAllocator::getMallocAllocator()); + glReadPixels(0, 0, m_desc.width, m_desc.height, GL_RGBA, GL_UNSIGNED_BYTE, surface.m_data); + surface.flipInplaceVertically(); + return PngSerializeUtil::write(outputPath, surface); } ShaderCompiler* GLRenderer::getShaderCompiler() diff --git a/tools/render-test/render-test.vcxproj b/tools/render-test/render-test.vcxproj index 3fecb6879..9889ba979 100644 --- a/tools/render-test/render-test.vcxproj +++ b/tools/render-test/render-test.vcxproj @@ -171,6 +171,7 @@ + @@ -180,6 +181,7 @@ + @@ -191,6 +193,7 @@ + @@ -200,6 +203,7 @@ + diff --git a/tools/render-test/render-test.vcxproj.filters b/tools/render-test/render-test.vcxproj.filters index 2bbf03af2..a8453035b 100644 --- a/tools/render-test/render-test.vcxproj.filters +++ b/tools/render-test/render-test.vcxproj.filters @@ -72,6 +72,12 @@ Source Files + + Source Files + + + Source Files + @@ -131,5 +137,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/tools/render-test/render-vk.cpp b/tools/render-test/render-vk.cpp index 9999d1e82..8ffb05c99 100644 --- a/tools/render-test/render-vk.cpp +++ b/tools/render-test/render-vk.cpp @@ -33,7 +33,7 @@ public: enum { kMaxRenderTargets = 8, kMaxAttachments = kMaxRenderTargets + 1 }; // Renderer implementation - virtual SlangResult initialize(void* inWindowHandle) override; + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; virtual void setClearColor(const float color[4]) override; virtual void clearFrame() override; virtual void presentFrame() override; @@ -254,6 +254,8 @@ public: int m_swapChainImageIndex = -1; float m_clearColor[4] = { 0, 0, 0, 0 }; + + Desc m_desc; }; /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer::Buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ @@ -774,11 +776,13 @@ VkPipelineShaderStageCreateInfo VKRenderer::compileEntryPoint(const ShaderCompil // !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! -SlangResult VKRenderer::initialize(void* inWindowHandle) +SlangResult VKRenderer::initialize(const Desc& desc, void* inWindowHandle) { SLANG_RETURN_ON_FAIL(m_module.init()); SLANG_RETURN_ON_FAIL(m_api.initGlobalProcs(m_module)); + m_desc = desc; + VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; applicationInfo.pApplicationName = "slang-render-test"; applicationInfo.pEngineName = "slang-render-test"; diff --git a/tools/render-test/render.h b/tools/render-test/render.h index d4385979a..ba76f795d 100644 --- a/tools/render-test/render.h +++ b/tools/render-test/render.h @@ -534,12 +534,13 @@ class Renderer: public Slang::RefObject { public: - struct Info + struct Desc { - ProjectionStyle m_projectionStyle; + int width; ///< Width in pixels + int height; ///< height in pixels }; - virtual SlangResult initialize(void* inWindowHandle) = 0; + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) = 0; virtual void setClearColor(const float color[4]) = 0; virtual void clearFrame() = 0; diff --git a/tools/render-test/surface.cpp b/tools/render-test/surface.cpp new file mode 100644 index 000000000..3f96052ce --- /dev/null +++ b/tools/render-test/surface.cpp @@ -0,0 +1,191 @@ +// surface.cpp +#include "surface.h" + +#include +#include + +#include "../../source/core/list.h" + +namespace renderer_test { +using namespace Slang; + +class MallocSurfaceAllocator: public SurfaceAllocator +{ + public: + + virtual Slang::Result allocate(int width, int height, Format format, int alignment, Surface& surface) override; + virtual void deallocate(Surface& surface) override; +}; + +static MallocSurfaceAllocator s_mallocSurfaceAllocator; + +/// Get the malloc allocator +/* static */SurfaceAllocator* SurfaceAllocator::getMallocAllocator() +{ + return &s_mallocSurfaceAllocator; +} + +Slang::Result MallocSurfaceAllocator::allocate(int width, int height, Format format, int alignment, Surface& surface) +{ + assert(surface.m_data == nullptr); + + // Calculate row size + + const int rowSizeInBytes = Surface::calcRowSize(format, width); + const int numRows = Surface::calcNumRows(format, height); + + alignment = (alignment <= 0) ? int(sizeof(void*)) : alignment; + // It must be a power of 2 + assert( ((alignment - 1) & alignment) == 0); + + // Align rowSize + const int alignedRowSizeInBytes = (rowSizeInBytes + alignment - 1) & -alignment; + + size_t totalSize = numRows * alignedRowSizeInBytes; + + uint8_t* data = (uint8_t*)::malloc(totalSize); + if (!data) + { + return SLANG_E_MEM_OUT_OF_MEMORY; + } + + surface.m_data = data; + surface.m_width = width; + surface.m_height = height; + surface.m_format = format; + surface.m_numRows = numRows; + surface.m_rowStrideInBytes = alignedRowSizeInBytes; + + surface.m_allocator = this; + return SLANG_OK; +} + +void MallocSurfaceAllocator::deallocate(Surface& surface) +{ + assert(surface.m_data); + // Make sure it's not an inverted, cos otherwise m_data is not the start address + assert(surface.m_rowStrideInBytes > 0); + ::free(surface.m_data); +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Surface !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +/* static */int Surface::calcRowSize(Format format, int width) +{ + size_t pixelSize = RendererUtil::getFormatSize(format); + if (pixelSize == 0) + { + return 0; + } + return int(pixelSize * width); +} + +/* static */int Surface::calcNumRows(Format format, int height) +{ + // Don't have any compressed types, so number of rows is same as the height + return height; +} + +void Surface::init() +{ + m_width = 0; + m_height = 0; + m_format = Format::Unknown; + m_data = nullptr; + m_numRows = 0; + m_rowStrideInBytes = 0; + // NOTE! does not clear the allocator. + // If called with an allocation memory will leak! +} + +Surface::~Surface() +{ + if (m_data && m_allocator) + { + m_allocator->deallocate(*this); + } +} + +void Surface::deallocate() +{ + if (m_data && m_allocator) + { + m_allocator->deallocate(*this); + init(); + } +} + +Result Surface::allocate(int width, int height, Format format, int alignment, SurfaceAllocator* allocator) +{ + deallocate(); + allocator = allocator ? allocator : m_allocator; + if (!allocator) + { + // An allocator needs to be set on the surface, or one passed in. + return SLANG_FAIL; + } + return allocator->allocate(width, height, format, alignment, *this); +} + +void Surface::setUnowned(int width, int height, Format format, int strideInBytes, void* data) +{ + deallocate(); + + // This is unowned + m_allocator = nullptr; + + m_width = width; + m_height = height; + m_format = format; + m_rowStrideInBytes = strideInBytes; + m_data = (uint8_t*)data; + + m_numRows = Surface::calcNumRows(format, height); + + const int rowSizeInBytes = Surface::calcRowSize(format, width); + assert((strideInBytes > 0 && rowSizeInBytes <= strideInBytes) || (strideInBytes < 0 && rowSizeInBytes <= -strideInBytes)); +} + +void Surface::zeroContents() +{ + const int rowSizeInBytes = Surface::calcRowSize(m_format, m_width); + + const int stride = m_rowStrideInBytes; + uint8_t* dst = m_data; + + for (int i = 0; i < m_numRows; i++, dst += stride) + { + ::memset(dst, 0, rowSizeInBytes); + } +} + +void Surface::flipInplaceVertically() +{ + // Can only flip when m_height matches number of rows + assert(m_numRows == m_height); + + const int rowSizeInBytes = Surface::calcRowSize(m_format, m_width); + if (rowSizeInBytes <= 0 || m_numRows <= 1) + { + return; + } + + uint8_t* top = m_data; + uint8_t* bottom = m_data + (m_numRows - 1) * m_rowStrideInBytes; + + List bufferList; + bufferList.SetSize(rowSizeInBytes); + uint8_t* buffer = bufferList.Buffer(); + + const int stride = m_rowStrideInBytes; + + const int num = m_height >> 1; + for (int i = 0; i < num; ++i, top += stride, bottom -= stride) + { + ::memcpy(buffer, top, rowSizeInBytes); + ::memcpy(top, bottom, rowSizeInBytes); + ::memcpy(bottom, buffer, rowSizeInBytes); + } +} + +} // renderer_test diff --git a/tools/render-test/surface.h b/tools/render-test/surface.h new file mode 100644 index 000000000..f5f67efc0 --- /dev/null +++ b/tools/render-test/surface.h @@ -0,0 +1,83 @@ +// surface.h +#pragma once + +#include "render.h" + +namespace renderer_test { + +class Surface; + +class SurfaceAllocator +{ + public: + virtual Slang::Result allocate(int width, int height, Format format, int alignment, Surface& surface) = 0; + virtual void deallocate(Surface& surface) = 0; + + /// Get the malloc allocator + static SurfaceAllocator* getMallocAllocator(); +}; + +class Surface +{ + public: + + enum + { + kDefaultAlignment = sizeof(void*) + }; + + /// Allocate + Slang::Result allocate(int width, int height, Format format, int alignment = kDefaultAlignment, SurfaceAllocator* allocator = nullptr); + + /// Deallocate contents + void deallocate(); + /// Initialize contents (zero sized, no data). Note that the allocator pointer is left as is + void init(); + + /// Set unowned + void setUnowned(int width, int height, Format format, int strideInBytes, void* data); + + template + T* calcNextRow(T* ptr) const { return (T*)calcNextRow((void*)ptr); } + template + const T* calcNextRow(const T* ptr) const { return (const T*)calcNextRow((const void*)ptr); } + + void* calcNextRow(void* ptr) const { return (void*)(((uint8_t*)ptr) + m_rowStrideInBytes); } + const void* calcNextRow(const void* ptr) const { return (const void*)(((const uint8_t*)ptr) + m_rowStrideInBytes); } + + /// Writes zero to all of the contents + void zeroContents(); + + /// Flips the contents vertically in place + void flipInplaceVertically(); + + /// True if has some contents + bool hasContents() const { return m_data != nullptr; } + + /// Ctor + Surface() : + m_allocator(nullptr) + { + init(); + } + /// Dtor + ~Surface(); + + /// Get the size of the row in bytes + static int calcRowSize(Format format, int width); + /// Calculates the number of rows + static int calcNumRows(Format format, int height); + + int m_width; + int m_height; + Format m_format; + + uint8_t* m_data; /// The data that makes up the image. If nullptr, has no data. Pointer to first 'row' of the image. + + int m_numRows; ///< Total amount of rows (typically same as height, but in compressed formats may be less) + int m_rowStrideInBytes; ///< The number of bytes between rows + + SurfaceAllocator* m_allocator; ///< Can be null if so contents is 'unowned', if set +}; + +} // renderer_test -- cgit v1.2.3