diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-02-02 07:49:32 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-02-02 07:49:32 -0800 |
| commit | b034398ab12d3cc3a5fc04174688cb707404791d (patch) | |
| tree | ffa3f01046a7dca55cf004855ca9424c82eaa46f /tools/render-test/render-vk.cpp | |
| parent | 652a3c987d2b42d069bf54ba251126208d00d9e7 (diff) | |
Initial work on getting render-test to support vulkan (#391)
* Basic fixes to gets some Vulkan GLSL out of the IR path
We haven't been paying much attention to the Vulkan output from the IR path, but that needs to change ASAP. This commit really just implements quick fixes, without concern for whether they are a good fit in the long term.
- Add some more mappings from D3D `SV_*` semantics to built-in GLSL variables, and stop redeclaring those built-in variables in our output GLSL.
- Add custom output logic for HLSL `*StructuredBuffer<T>` types, so that they emit as `buffer` declarations with an unsized array inside. This has some real limitations:
- What if the user passes the type into a function? The parameter should be typed as an (unsized) array, and not a buffer.
- What happens if we have an array of structured buffers? We need to declare an array of blocks (which GLSL allows), but this changes the GLSL we should emit when indexing.
- Customize the way that we emit entry point attributes (e.g., `[numthread(...)]`) to also support outputting equivalent GLSL `layout` qualifiers.
In many of these cases, a better fix might involve doing more of this work in the IR as part of legalization (e.g., we already have a pass that deals with varying input/output for GLSL, so that should probalby be responsible for swapping the `SV_*` to `gl_*`, especially in cases where the types don't match perfectly across langauges).
* Start adding Vulkan support to render-test
- Add both Vulkan and D3D12 as nominally supported back-ends
- Add a git submodule to pull in the Vulkan SDK dependencies
- I don't want our users to have to install it manually, since the SDK is huge
- Checking in the binaries to our main repository seems like a bad idea, but my hope is that we can prune the bloat using a subodule with the `shallow` cloning option
- Implement enough logic for the Vulkan back-end to get a single test passing on Vulkan
* Fix warning
* Fixup: disable new compute tests for Linux
* Fixup: ignore Vulkan tests on AppVeyor
* Dynamically load Vulkan implementation
Rather than statically link to the Vulkan library, we will dynamically load all of the required functions.
This removes the need to have the stub libs involved at all.
* Remove vulkan submodule
I had set up a `vulkan` submodule to pull in the headers and stub libs, but now that we are going to dynamically load all the symbols anyway, the stub lib binaries aren't needed and we can just commit the headers.
* Add Vulkan headers to external/
Diffstat (limited to 'tools/render-test/render-vk.cpp')
| -rw-r--r-- | tools/render-test/render-vk.cpp | 1033 |
1 files changed, 1033 insertions, 0 deletions
diff --git a/tools/render-test/render-vk.cpp b/tools/render-test/render-vk.cpp new file mode 100644 index 000000000..fd080931d --- /dev/null +++ b/tools/render-test/render-vk.cpp @@ -0,0 +1,1033 @@ +// render-vk.cpp +#include "render-vk.h" + +#include "options.h" +#include "render.h" + +#ifdef _WIN32 +#define VK_USE_PLATFORM_WIN32_KHR 1 +#endif + +#define VK_NO_PROTOTYPES +#include <vulkan/vulkan.h> + +#define ENABLE_VALIDATION_LAYER 1 + + +#ifdef _MSC_VER + +#include <stddef.h> + +#pragma warning(disable: 4996) + +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif + +#define FOREACH_GLOBAL_PROC(M) \ + M(vkCreateInstance) \ + /* */ + +#define FOREACH_INSTANCE_PROC(M) \ + M(vkCreateDevice) \ + M(vkCreateDebugReportCallbackEXT) \ + M(vkDestroyDebugReportCallbackEXT) \ + M(vkDebugReportMessageEXT) \ + M(vkEnumeratePhysicalDevices) \ + M(vkGetPhysicalDeviceProperties) \ + M(vkGetPhysicalDeviceFeatures) \ + M(vkGetPhysicalDeviceMemoryProperties) \ + M(vkGetPhysicalDeviceQueueFamilyProperties) \ + M(vkGetDeviceProcAddr) \ + /* */ + +#define FOREACH_DEVICE_PROC(M) \ + M(vkCreateDescriptorPool) \ + M(vkCreateCommandPool) \ + M(vkGetDeviceQueue) \ + M(vkAllocateCommandBuffers) \ + M(vkBeginCommandBuffer) \ + M(vkEndCommandBuffer) \ + M(vkQueueSubmit) \ + M(vkQueueWaitIdle) \ + M(vkFreeCommandBuffers) \ + M(vkCreateBuffer) \ + M(vkGetBufferMemoryRequirements) \ + M(vkAllocateMemory) \ + M(vkBindBufferMemory) \ + M(vkMapMemory) \ + M(vkUnmapMemory) \ + M(vkCmdCopyBuffer) \ + M(vkDestroyBuffer) \ + M(vkFreeMemory) \ + M(vkCreateDescriptorSetLayout) \ + M(vkAllocateDescriptorSets) \ + M(vkUpdateDescriptorSets) \ + M(vkCreatePipelineLayout) \ + M(vkCreateComputePipelines) \ + M(vkCmdBindPipeline) \ + M(vkCmdBindDescriptorSets) \ + M(vkCmdDispatch) \ + M(vkDestroyPipeline) \ + M(vkCreateShaderModule) \ + /* */ + +namespace renderer_test { + +class VKRenderer : public Renderer, public ShaderCompiler +{ +public: + + VkInstance instance; + VkPhysicalDevice physicalDevice; + VkPhysicalDeviceProperties deviceProperties; + VkPhysicalDeviceFeatures deviceFeatures; + VkPhysicalDeviceMemoryProperties deviceMemoryProperties; + VkPhysicalDeviceFeatures enabledFeatures; + VkDevice device; + VkQueue queue; + VkCommandPool commandPool; + VkSubmitInfo submitInfo; + VkDebugReportCallbackEXT debugReportCallback; + + +#define DECLARE_PROC(NAME) PFN_##NAME NAME; + DECLARE_PROC(vkGetInstanceProcAddr); + FOREACH_GLOBAL_PROC(DECLARE_PROC) + FOREACH_INSTANCE_PROC(DECLARE_PROC) + FOREACH_DEVICE_PROC(DECLARE_PROC) +#undef DECLARE_PROC + + // Renderer interface + + void checkResult(VkResult result) + { + assert(result == VK_SUCCESS); + } + + VkBool32 handleDebugMessage( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objType, + uint64_t srcObject, + size_t location, + int32_t msgCode, + const char* pLayerPrefix, + const char* pMsg) + { + char const* severity = "message"; + if(flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) + severity = "warning"; + if(flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) + severity = "error"; + + char buffer[1024]; + sprintf_s(buffer, + "%s: %s %d: %s\n", + pLayerPrefix, + severity, + msgCode, + pMsg); + + fprintf(stderr, "%s", buffer); + fflush(stderr); + + OutputDebugStringA(buffer); + + return VK_FALSE; + } + + static VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objType, + uint64_t srcObject, + size_t location, + int32_t msgCode, + const char* pLayerPrefix, + const char* pMsg, + void* pUserData) + { + return ((VKRenderer*) pUserData)->handleDebugMessage( + flags, objType, srcObject, location, msgCode, pLayerPrefix, pMsg); + } + + virtual void initialize(void* inWindowHandle) override + { + char const* dynamicLibraryName = "vulkan-1.dll"; + HMODULE vulkan = LoadLibraryA(dynamicLibraryName); + if(!vulkan) + { + fprintf(stderr, "error: failed load '%s'\n", dynamicLibraryName); + exit(1); + } + + vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(vulkan, "vkGetInstanceProcAddr"); + if(!vkGetInstanceProcAddr) + { + fprintf(stderr, + "error: failed load symbol 'vkGetInstanceProcAddr'\n"); + exit(1); + } + + VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; + applicationInfo.pApplicationName = "slang-render-test"; + applicationInfo.pEngineName = "slang-render-test"; + applicationInfo.apiVersion = VK_API_VERSION_1_0; + + char const* instanceExtensions[] = { + VK_KHR_SURFACE_EXTENSION_NAME, +#ifdef _WIN32 + VK_KHR_WIN32_SURFACE_EXTENSION_NAME, +#else +#endif + +#if ENABLE_VALIDATION_LAYER + VK_EXT_DEBUG_REPORT_EXTENSION_NAME, +#endif + }; + + VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; + instanceCreateInfo.pApplicationInfo = &applicationInfo; + + instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensions) / sizeof(instanceExtensions[0]); + instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0]; + +#if ENABLE_VALIDATION_LAYER + + uint32_t layerCount = 1; + const char *layerNames[] = { + "VK_LAYER_LUNARG_standard_validation" + }; + + instanceCreateInfo.enabledLayerCount = layerCount; + instanceCreateInfo.ppEnabledLayerNames = layerNames; +#endif + + instance = 0; + +#define LOAD_INSTANCE_PROC(NAME) NAME = (PFN_##NAME) vkGetInstanceProcAddr(instance, #NAME); + + FOREACH_GLOBAL_PROC(LOAD_INSTANCE_PROC); + + checkResult(vkCreateInstance( + &instanceCreateInfo, + nullptr, + &instance)); + + FOREACH_INSTANCE_PROC(LOAD_INSTANCE_PROC); + +#undef LOAD_INSTANCE_PROC + + +#if ENABLE_VALIDATION_LAYER + VkDebugReportFlagsEXT debugFlags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + + VkDebugReportCallbackCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; + debugCreateInfo.pfnCallback = &debugMessageCallback; + debugCreateInfo.pUserData = this; + debugCreateInfo.flags = debugFlags; + + checkResult(vkCreateDebugReportCallbackEXT( + instance, &debugCreateInfo, nullptr, &debugReportCallback)); + +#endif + + uint32_t physicalDeviceCount = 0; + checkResult(vkEnumeratePhysicalDevices( + instance, &physicalDeviceCount, nullptr)); + + VkPhysicalDevice* physicalDevices = (VkPhysicalDevice*)alloca( + physicalDeviceCount * sizeof(VkPhysicalDevice)); + checkResult(vkEnumeratePhysicalDevices( + instance, &physicalDeviceCount, physicalDevices)); + + uint32_t selectedDeviceIndex = 0; + // TODO: allow override of selected device + physicalDevice = physicalDevices[selectedDeviceIndex]; + + vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); + vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties( + physicalDevice, &queueFamilyCount, nullptr); + + VkQueueFamilyProperties* queueFamilies = (VkQueueFamilyProperties*)alloca( + queueFamilyCount * sizeof(VkQueueFamilyProperties)); + vkGetPhysicalDeviceQueueFamilyProperties( + physicalDevice, &queueFamilyCount, queueFamilies); + + // Find a queue that can service our needs + VkQueueFlags reqQueueFlags = + VK_QUEUE_GRAPHICS_BIT + | VK_QUEUE_COMPUTE_BIT; + + uint32_t queueFamilyIndex = uint32_t(-1); + for (uint32_t qq = 0; qq < queueFamilyCount; ++qq) + { + if ((queueFamilies[qq].queueFlags & reqQueueFlags) == reqQueueFlags) + { + queueFamilyIndex = qq; + break; + } + } + assert(queueFamilyIndex < queueFamilyCount); + + float queuePriority = 0.0f; + VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; + queueCreateInfo.queueFamilyIndex = queueFamilyIndex; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + + char const* const deviceExtensions[] = + { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; + deviceCreateInfo.queueCreateInfoCount = 1; + deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; + deviceCreateInfo.pEnabledFeatures = &enabledFeatures; + + deviceCreateInfo.enabledExtensionCount = sizeof(deviceExtensions) / sizeof(deviceExtensions[0]); + deviceCreateInfo.ppEnabledExtensionNames = &deviceExtensions[0]; + + checkResult(vkCreateDevice( + physicalDevice, &deviceCreateInfo, nullptr, &device)); + +#define LOAD_DEVICE_PROC(NAME) NAME = (PFN_##NAME) vkGetDeviceProcAddr(device, #NAME); + FOREACH_DEVICE_PROC(LOAD_DEVICE_PROC) +#undef LOAD_DEVICE_PROC + + // Create a command pool + + VkCommandPoolCreateInfo commandPoolCreateInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; + commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex; + commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + checkResult(vkCreateCommandPool( + device, &commandPoolCreateInfo, nullptr, &commandPool)); + + vkGetDeviceQueue( + device, + queueFamilyIndex, + 0, + &queue); + + // set up swap chain + + // create command buffers + + // depth/stencil? + + // render pass? + + // pipeline cache + + // frame buffer + + + +// create semaphores for sync + + + } + + float clearColor[4]; + virtual void setClearColor(float const* color) override + { + for(int ii = 0; ii < 4; ++ii) + clearColor[ii] = color[ii]; + } + + virtual void clearFrame() override + { + } + + virtual void presentFrame() override + { + } + + virtual void captureScreenShot(char const* outputPath) override + { + } + + virtual ShaderCompiler* getShaderCompiler() override + { + return this; + } + + VkCommandBuffer getCommandBuffer() + { + VkCommandBufferAllocateInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + info.commandPool = commandPool; + info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + info.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + checkResult(vkAllocateCommandBuffers( + device, &info, &commandBuffer)); + + return commandBuffer; + } + + VkCommandBuffer beginCommandBuffer() + { + VkCommandBuffer commandBuffer = getCommandBuffer(); + + VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + checkResult(vkBeginCommandBuffer(commandBuffer, &beginInfo)); + + return commandBuffer; + } + + void flushCommandBuffer(VkCommandBuffer commandBuffer) + { + checkResult(vkEndCommandBuffer(commandBuffer)); + + VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + checkResult(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + checkResult(vkQueueWaitIdle(queue)); + + vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); + } + + struct BufferImpl + { + VkBuffer buffer; + VkDeviceMemory memory; + }; + + BufferImpl createBufferImpl( + size_t bufferSize, + VkBufferUsageFlags usage, + VkMemoryPropertyFlags reqMemoryProperties, + void const* initData = nullptr) + { + if( initData ) + { + // TODO: what if we are allocating it as CPU-writable anyway? + usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + } + + VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + bufferCreateInfo.size = bufferSize; + bufferCreateInfo.usage = usage; + + VkBuffer buffer; + checkResult(vkCreateBuffer( + device, &bufferCreateInfo, nullptr, &buffer)); + + VkMemoryRequirements memoryReqs = {}; + vkGetBufferMemoryRequirements(device, buffer, &memoryReqs); + + uint32_t memoryTypeIndex = getMemoryTypeIndex( + memoryReqs.memoryTypeBits, reqMemoryProperties); + + VkMemoryPropertyFlags actualMemoryProperites = deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; + + VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocateInfo.allocationSize = memoryReqs.size; + allocateInfo.memoryTypeIndex = memoryTypeIndex; + + VkDeviceMemory memory; + checkResult(vkAllocateMemory( + device, &allocateInfo, nullptr, &memory)); + + checkResult(vkBindBufferMemory( + device, buffer, memory, 0)); + + if( initData ) + { + // TODO: only create staging buffer if the memory type + // used for the buffer doesn't let us fill things in + // directly. + + BufferImpl staging = createBufferImpl( + bufferSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + // Copy into staging buffer + void* mappedData = nullptr; + checkResult(vkMapMemory(device, staging.memory, 0, bufferSize, 0, &mappedData)); + memcpy(mappedData, initData, bufferSize); + vkUnmapMemory(device, staging.memory); + + // Copy from staging buffer to real buffer + VkCommandBuffer commandBuffer = beginCommandBuffer(); + + VkBufferCopy copyInfo = {}; + copyInfo.size = bufferSize; + vkCmdCopyBuffer( + commandBuffer, + staging.buffer, + buffer, + 1, + ©Info); + + flushCommandBuffer(commandBuffer); + + // Now destroy the staging buffer + vkDestroyBuffer(device, staging.buffer, nullptr); + vkFreeMemory(device, staging.memory, nullptr); + } + + BufferImpl impl; + impl.buffer = buffer; + impl.memory = memory; + return impl; + } + + virtual Buffer* createBuffer(BufferDesc const& desc) override + { + size_t bufferSize = desc.size; + + VkBufferUsageFlags usage = 0; + VkMemoryPropertyFlags reqMemoryProperties = 0; + + switch( desc.flavor ) + { + case BufferFlavor::Constant: + usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + + case BufferFlavor::Vertex: + usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + reqMemoryProperties = 0; + break; + } + + BufferImpl bufferImpl = createBufferImpl( + bufferSize, + usage, + reqMemoryProperties, + desc.initData); + + BufferImpl* bufferPtr = new BufferImpl(); + *bufferPtr = bufferImpl; + return (Buffer*) bufferPtr; + } + + struct InputLayoutImpl + { + }; + + virtual InputLayout* createInputLayout(InputElementDesc const* inputElements, UInt inputElementCount) override + { + InputLayoutImpl* impl = new InputLayoutImpl(); + + // TODO: actually initialize things + + return (InputLayout*) impl; + } + + virtual void* map(Buffer* buffer, MapFlavor flavor) override + { + return nullptr; + } + + virtual void unmap(Buffer* buffer) override + { + } + + virtual void setInputLayout(InputLayout* inputLayout) override + { + } + + virtual void setPrimitiveTopology(PrimitiveTopology topology) override + { + } + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* strides, UInt const* offsets) override + { + } + + struct ShaderProgramImpl + { + VkPipelineShaderStageCreateInfo compute; + VkPipelineShaderStageCreateInfo vertex; + VkPipelineShaderStageCreateInfo fragment; + }; + + ShaderProgramImpl* currentProgram = nullptr; + virtual void setShaderProgram(ShaderProgram* program) override + { + currentProgram = (ShaderProgramImpl*) program; + } + + virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override + { + } + + virtual void draw(UInt vertexCount, UInt startVertex = 0) override + { + } + + struct BindingImpl + { + ShaderInputType type; + InputBufferType bufferType; // Only valid if `type` is `Buffer` + + VkImageView srv; + VkBufferView uav; + VkBuffer buffer; + VkSampler samplerState; + + int binding = 0; + bool isOutput = false; + int bufferLength = 0; + }; + + struct BindingStateImpl + { + Slang::List<BindingImpl> bindings; + int numRenderTargets; + }; + + uint32_t getMemoryTypeIndex( + uint32_t inTypeBits, + VkMemoryPropertyFlags properties) + { + uint32_t typeBits = inTypeBits; + uint32_t typeIndex = 0; + while( typeBits ) + { + if((deviceMemoryProperties.memoryTypes[typeIndex].propertyFlags & properties) == properties) + { + return typeIndex; + } + typeIndex++; + typeBits >>= 1; + } + + assert(!"failed to find a usable memory type"); + return uint32_t(-1); + } + + void createInputBuffer( + ShaderInputLayoutEntry const& entry, + InputBufferDesc const& bufferDesc, + Slang::List<unsigned int> const& bufferData, + VkBuffer &bufferOut, + VkBufferView &uavOut, + VkImageView &srvOut) + { + size_t bufferSize = bufferData.Count() * sizeof(unsigned int); + void const* initData = bufferData.Buffer(); + + VkBufferUsageFlags usage = 0; + VkMemoryPropertyFlags reqMemoryProperties = 0; + + switch( bufferDesc.type ) + { + case InputBufferType::ConstantBuffer: + usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + + case InputBufferType::StorageBuffer: + usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + reqMemoryProperties = 0; + break; + } + + // If we are going to read back from the buffer, be sure to request + // the required access. + if(entry.isOutput) + { + usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + } + + BufferImpl bufferImpl = createBufferImpl( + bufferSize, + usage, + reqMemoryProperties, + initData); + + // TODO: need to hang onto the `memory` field so + // that we can release it when we are done. + + bufferOut = bufferImpl.buffer; + + // Fill in any views needed + switch( bufferDesc.type ) + { + case InputBufferType::ConstantBuffer: + break; + + case InputBufferType::StorageBuffer: + { + } + break; + } + } + + void createInputTexture( + InputTextureDesc const& inputDesc, + VkImageView& viewOut) + { + TextureData texData; + generateTextureData(texData, inputDesc); + assert(!"unimplemented"); + } + + void createInputSampler( + InputSamplerDesc const& inputDesc, + VkSampler& stateOut) + { + assert(!"unimplemented"); + } + + virtual BindingState* createBindingState(const ShaderInputLayout & layout) + { + BindingStateImpl* bindingState = new BindingStateImpl(); + bindingState->numRenderTargets = layout.numRenderTargets; + for (auto & entry : layout.entries) + { + BindingImpl binding; + binding.type = entry.type; + binding.binding = entry.hlslBinding; + binding.isOutput = entry.isOutput; + switch (entry.type) + { + case ShaderInputType::Buffer: + { + createInputBuffer(entry, entry.bufferDesc, entry.bufferData, binding.buffer, binding.uav, binding.srv); + binding.bufferLength = (int)(entry.bufferData.Count() * sizeof(unsigned int)); + binding.bufferType = entry.bufferDesc.type; + } + break; + case ShaderInputType::Texture: + { + createInputTexture(entry.textureDesc, binding.srv); + } + break; + case ShaderInputType::Sampler: + { + createInputSampler(entry.samplerDesc, binding.samplerState); + } + break; + case ShaderInputType::CombinedTextureSampler: + { + throw "not implemented"; + } + break; + } + bindingState->bindings.Add(binding); + } + + return (BindingState*) bindingState; + } + + BindingStateImpl* currentBindingState = nullptr; + virtual void setBindingState(BindingState * state) + { + currentBindingState = (BindingStateImpl*) state; + } + + virtual void serializeOutput(BindingState* s, const char * fileName) + { + auto state = (BindingStateImpl*) s; + + FILE * f = fopen(fileName, "wb"); + int id = 0; + for (auto& bb: state->bindings) + { + if (bb.isOutput) + { + if (bb.buffer) + { + // create staging buffer + size_t bufferSize = bb.bufferLength; + BufferImpl staging = createBufferImpl( + bufferSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + // Copy from real buffer to staging buffer + VkCommandBuffer commandBuffer = beginCommandBuffer(); + + VkBufferCopy copyInfo = {}; + copyInfo.size = bufferSize; + vkCmdCopyBuffer( + commandBuffer, + bb.buffer, + staging.buffer, + 1, + ©Info); + + flushCommandBuffer(commandBuffer); + + // Write out the data from the buffer + void* mappedData = nullptr; + checkResult(vkMapMemory(device, staging.memory, 0, bufferSize, 0, &mappedData)); + + auto ptr = (unsigned int *) mappedData; + for (auto i = 0u; i < bufferSize / sizeof(unsigned int); i++) + fprintf(f, "%X\n", ptr[i]); + + vkUnmapMemory(device, staging.memory); + + // Now destroy the staging buffer + vkDestroyBuffer(device, staging.buffer, nullptr); + vkFreeMemory(device, staging.memory, nullptr); + } + else + { + printf("invalid output type at %d.\n", id); + } + } + id++; + } + fclose(f); + + + } + + virtual void dispatchCompute(int x, int y, int z) override + { + + + // HACK: create a new pipeline for every call + + // First create a pipeline layout based on what is bound + + Slang::List<VkDescriptorSetLayoutBinding> bindings; + + for( auto bb : currentBindingState->bindings ) + { + switch( bb.type ) + { + case ShaderInputType::Buffer: + { + switch(bb.bufferType) + { + case InputBufferType::StorageBuffer: + { + VkDescriptorSetLayoutBinding binding = {}; + binding.binding = bb.binding; + binding.descriptorCount = 1; + binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + binding.stageFlags = VK_SHADER_STAGE_ALL; + + bindings.Add(binding); + } + break; + + default: + // handle other cases + break; + } + } + break; + + default: + // TODO: handle the other cases + break; + } + } + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + descriptorSetLayoutInfo.bindingCount = uint32_t(bindings.Count()); + descriptorSetLayoutInfo.pBindings = bindings.Buffer(); + + VkDescriptorSetLayout descriptorSetLayout = 0; + checkResult(vkCreateDescriptorSetLayout( + device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout)); + + // Create a descriptor pool for allocating sets + + VkDescriptorPoolSize poolSizes[] = + { + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 128 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 128 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 128 }, + }; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size + descriptorPoolInfo.poolSizeCount = sizeof(poolSizes) / sizeof(poolSizes[0]); + descriptorPoolInfo.pPoolSizes = poolSizes; + + VkDescriptorPool descriptorPool; + checkResult(vkCreateDescriptorPool( + device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + // Create a descriptor set based on our layout + + VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + descriptorSetAllocInfo.descriptorPool = descriptorPool; + descriptorSetAllocInfo.descriptorSetCount = 1; + descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout; + + VkDescriptorSet descriptorSet; + checkResult(vkAllocateDescriptorSets( + device, &descriptorSetAllocInfo, &descriptorSet)); + + // Fill in the descritpor set, using our binding information + for( auto bb : currentBindingState->bindings ) + { + switch( bb.type ) + { + case ShaderInputType::Buffer: + { + switch(bb.bufferType) + { + case InputBufferType::StorageBuffer: + { + VkDescriptorBufferInfo bufferInfo; + bufferInfo.buffer = bb.buffer; + bufferInfo.offset = 0; + bufferInfo.range = bb.bufferLength; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + writeInfo.dstSet = descriptorSet; + writeInfo.dstBinding = bb.binding; + writeInfo.dstArrayElement = 0; + writeInfo.pBufferInfo = &bufferInfo; + + vkUpdateDescriptorSets( + device, + 1, + &writeInfo, + 0, + nullptr); + } + break; + + default: + // handle other cases + break; + } + } + break; + + default: + // TODO: handle the other cases + break; + } + } + + + // Create a pipeline layout based on our descriptor set layout(s) + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; + + VkPipelineLayout pipelineLayout = 0; + checkResult(vkCreatePipelineLayout( + device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); + + // Then create a pipeline to use that layout + + VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; + computePipelineInfo.stage = currentProgram->compute; + computePipelineInfo.layout = pipelineLayout; + + VkPipelineCache pipelineCache = 0; + + VkPipeline pipeline; + checkResult(vkCreateComputePipelines( + device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline)); + + // Also create descriptor sets based on the given pipeline layout + + VkCommandBuffer commandBuffer = beginCommandBuffer(); + + vkCmdBindPipeline( + commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); + + vkCmdBindDescriptorSets( + commandBuffer, + VK_PIPELINE_BIND_POINT_COMPUTE, + pipelineLayout, + 0, 1, + &descriptorSet, + 0, + nullptr); + + vkCmdDispatch(commandBuffer, x, y, z); + + flushCommandBuffer(commandBuffer); + + vkDestroyPipeline(device, pipeline, nullptr); + + // TODO: need to free up the other resources too... + } + + // ShaderCompiler interface + + VkPipelineShaderStageCreateInfo compileEntryPoint( + ShaderCompileRequest::EntryPoint const& entryPointRequest, + VkShaderStageFlagBits stage) + { + char const* dataBegin = entryPointRequest.source.dataBegin; + char const* dataEnd = entryPointRequest.source.dataEnd; + + // We need to make a copy of the code, since the Slang compiler + // will free the memory after a compile request is closed. + size_t codeSize = dataEnd - dataBegin; + char* codeBegin = (char*) malloc(codeSize); + memcpy(codeBegin, dataBegin, codeSize); + + VkShaderModuleCreateInfo moduleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; + moduleCreateInfo.pCode = (uint32_t*) codeBegin; + moduleCreateInfo.codeSize = codeSize; + + VkShaderModule module; + checkResult(vkCreateShaderModule( + device, + &moduleCreateInfo, + nullptr, + &module)); + + VkPipelineShaderStageCreateInfo shaderStageCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; + shaderStageCreateInfo.stage = stage; + + shaderStageCreateInfo.module = module; + shaderStageCreateInfo.pName = "main"; + return shaderStageCreateInfo; + } + + virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override + { + ShaderProgramImpl* impl = new ShaderProgramImpl(); + + if( request.computeShader.name ) + { + impl->compute = compileEntryPoint( + request.computeShader, + VK_SHADER_STAGE_COMPUTE_BIT); + } + else + { + impl->vertex = compileEntryPoint( + request.vertexShader, + VK_SHADER_STAGE_VERTEX_BIT); + + impl->fragment = compileEntryPoint( + request.fragmentShader, + VK_SHADER_STAGE_FRAGMENT_BIT); + } + + return (ShaderProgram*) impl; + } +}; + + + +Renderer* createVKRenderer() +{ + return new VKRenderer(); +} + +} // renderer_test |
