diff --git a/include/application.hpp b/include/application.hpp index 93180ef..07b96d2 100644 --- a/include/application.hpp +++ b/include/application.hpp @@ -28,12 +28,16 @@ namespace engine { struct RenderData { std::unique_ptr gfxdev; gfx::DrawBuffer* drawBuffer = nullptr; + /* uniforms for engine globals */ const gfx::DescriptorSetLayout* setZeroLayout; const gfx::DescriptorSet* setZero; struct SetZeroBuffer { glm::mat4 proj; }; + const gfx::Image* myImage = nullptr; + const gfx::Sampler* mySampler = nullptr; + gfx::UniformBuffer* setZeroBuffer; /* uniforms for per-frame data */ const gfx::DescriptorSetLayout* setOneLayout; diff --git a/include/gfx.hpp b/include/gfx.hpp index 11779e7..38979c3 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -17,6 +17,7 @@ namespace engine::gfx { struct DescriptorSetLayout; struct DescriptorSet; struct Image; + struct Sampler; enum class MSAALevel { MSAA_OFF, diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index f23e73e..a63344a 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -38,7 +38,7 @@ namespace engine { gfx::DescriptorSet* allocateDescriptorSet(const gfx::DescriptorSetLayout* layout); // This updates all copies of the descriptor. This cannot be used after any frames have been renderered void updateDescriptorUniformBuffer(const gfx::DescriptorSet* set, uint32_t binding, const gfx::UniformBuffer* buffer, size_t offset, size_t range); - void updateDescriptorCombinedImageSampler(const gfx::DescriptorSet* set, uint32_t binding); + void updateDescriptorCombinedImageSampler(const gfx::DescriptorSet* set, uint32_t binding, const gfx::Image* image, const gfx::Sampler* sampler); gfx::UniformBuffer* createUniformBuffer(uint64_t size, const void* initialData); void destroyUniformBuffer(const gfx::UniformBuffer* descriptorBuffer); @@ -50,6 +50,10 @@ namespace engine { void destroyBuffer(const gfx::Buffer* buffer); gfx::Image* createImage(uint32_t w, uint32_t h, const void* imageData); + void destroyImage(const gfx::Image* image); + + gfx::Sampler* createSampler(); + void destroySampler(const gfx::Sampler* sampler); gfx::Texture* createTexture( const void* imageData, diff --git a/res/engine/shaders/standard.frag b/res/engine/shaders/standard.frag index 91f931a..df6d444 100644 --- a/res/engine/shaders/standard.frag +++ b/res/engine/shaders/standard.frag @@ -7,7 +7,7 @@ layout(location = 3) in vec3 fragLightPos; layout(location = 0) out vec4 outColor; -//layout(set = 1, binding = 0) uniform sampler2D texSampler; +layout(set = 0, binding = 1) uniform sampler2D texSampler; void main() { @@ -15,8 +15,7 @@ void main() { vec3 lightColor = vec3(1.0, 1.0, 1.0); vec3 ambientColor = vec3(1.0, 1.0, 1.0); float ambientStrength = 0.05; - //vec3 baseColor = vec3(texture(texSampler, fragUV)); - vec3 baseColor = vec3(mod(fragUV.x, 1.0), mod(fragUV.y, 1.0), 0.0); + vec3 baseColor = vec3(texture(texSampler, fragUV)); vec3 emission = vec3(0.0, 0.0, 0.0); // code diff --git a/src/application.cpp b/src/application.cpp index e0c87ae..cda7fef 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -76,22 +76,33 @@ namespace engine { // initialise the render data renderData.gfxdev = std::make_unique(appName, appVersion, m_window->getHandle(), graphicsSettings); - std::vector layoutBindings; + std::vector setZeroLayoutBindings; { - auto& binding0 = layoutBindings.emplace_back(); + auto& binding0 = setZeroLayoutBindings.emplace_back(); binding0.descriptorType = gfx::DescriptorType::UNIFORM_BUFFER; binding0.stageFlags = gfx::ShaderStageFlags::VERTEX; + auto& binding1 = setZeroLayoutBindings.emplace_back(); + binding1.descriptorType = gfx::DescriptorType::COMBINED_IMAGE_SAMPLER; + binding1.stageFlags = gfx::ShaderStageFlags::FRAGMENT; } - - renderData.setZeroLayout = gfx()->createDescriptorSetLayout(layoutBindings); + renderData.setZeroLayout = gfx()->createDescriptorSetLayout(setZeroLayoutBindings); renderData.setZero = gfx()->allocateDescriptorSet(renderData.setZeroLayout); RenderData::SetZeroBuffer initialSetZeroData{ .proj = glm::perspectiveZO(glm::radians(70.0f), 1024.0f / 768.0f, 0.1f, 1000.0f), }; renderData.setZeroBuffer = gfx()->createUniformBuffer(sizeof(RenderData::SetZeroBuffer), &initialSetZeroData); gfx()->updateDescriptorUniformBuffer(renderData.setZero, 0, renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer)); + renderData.myImage = gfx()->createImage(512, 512, nullptr); + renderData.mySampler = gfx()->createSampler(); + gfx()->updateDescriptorCombinedImageSampler(renderData.setZero, 1, renderData.myImage, renderData.mySampler); - renderData.setOneLayout = gfx()->createDescriptorSetLayout(layoutBindings); + std::vector setOneLayoutBindings; + { + auto& binding0 = setOneLayoutBindings.emplace_back(); + binding0.descriptorType = gfx::DescriptorType::UNIFORM_BUFFER; + binding0.stageFlags = gfx::ShaderStageFlags::VERTEX; + } + renderData.setOneLayout = gfx()->createDescriptorSetLayout(setOneLayoutBindings); renderData.setOne = gfx()->allocateDescriptorSet(renderData.setOneLayout); RenderData::SetOneBuffer initialSetOneData{ .view = glm::mat4{ 1.0f }, @@ -144,6 +155,9 @@ namespace engine { { gfx()->destroyUniformBuffer(renderData.setOneBuffer); gfx()->destroyDescriptorSetLayout(renderData.setOneLayout); + + gfx()->destroySampler(renderData.mySampler); + gfx()->destroyImage(renderData.myImage); gfx()->destroyUniformBuffer(renderData.setZeroBuffer); gfx()->destroyDescriptorSetLayout(renderData.setZeroLayout); } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 99291aa..6b9a185 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -94,6 +94,16 @@ namespace engine { VkPipeline handle = VK_NULL_HANDLE; }; + struct gfx::Image { + VkImage image = VK_NULL_HANDLE; + VkImageView view = VK_NULL_HANDLE; + VmaAllocation allocation = VK_NULL_HANDLE; + }; + + struct gfx::Sampler { + VkSampler sampler = VK_NULL_HANDLE; + }; + struct gfx::Texture { }; @@ -362,7 +372,9 @@ namespace engine { .format = VK_FORMAT_R8G8B8A8_SRGB, .properties = VkFormatProperties{ .linearTilingFeatures = {}, - .optimalTilingFeatures = VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, + .optimalTilingFeatures = + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | + VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT, .bufferFeatures = {}, } } @@ -1084,13 +1096,13 @@ namespace engine { } } - void GFXDevice::updateDescriptorCombinedImageSampler(const gfx::DescriptorSet *set, uint32_t binding) + void GFXDevice::updateDescriptorCombinedImageSampler(const gfx::DescriptorSet *set, uint32_t binding, const gfx::Image* image, const gfx::Sampler* sampler) { assert(pimpl->FRAMECOUNT == 0); VkDescriptorImageInfo imageInfo{}; - imageInfo.sampler = VK_NULL_HANDLE; - imageInfo.imageView = VK_NULL_HANDLE; + imageInfo.sampler = sampler->sampler; + imageInfo.imageView = image->view; imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { @@ -1259,13 +1271,138 @@ namespace engine { gfx::Image* GFXDevice::createImage(uint32_t w, uint32_t h, const void* imageData) { - (void)w; - (void)h; (void)imageData; - return nullptr; + + assert(pimpl->FRAMECOUNT == 0); + + gfx::Image* out = new gfx::Image{}; + + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.flags = 0; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB; + imageInfo.extent.width = w; + imageInfo.extent.height = h; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VmaAllocationCreateInfo allocCreateInfo{}; + allocCreateInfo.flags = 0; + allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + allocCreateInfo.priority = 0.5f; + + VKCHECK(vmaCreateImage(pimpl->allocator, &imageInfo, &allocCreateInfo, &out->image, &out->allocation, nullptr)); + + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = out->image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = imageInfo.format; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + VKCHECK(vkCreateImageView(pimpl->device.device, &viewInfo, nullptr, &out->view)); + + // Do a pipeline barrier to transition the layout + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = pimpl->transferCommandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + VKCHECK(vkAllocateCommandBuffers(pimpl->device.device, &allocInfo, &commandBuffer)); + + { // record the command buffer + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + VKCHECK(vkBeginCommandBuffer(commandBuffer, &beginInfo)); + + VkImageMemoryBarrier2 barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + barrier.srcStageMask = VK_PIPELINE_STAGE_2_NONE; + barrier.srcAccessMask = VK_ACCESS_2_NONE; + barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; + barrier.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcQueueFamilyIndex = pimpl->device.queues.transferQueueFamily; + barrier.dstQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily; + barrier.image = out->image; + barrier.subresourceRange = viewInfo.subresourceRange; + + VkDependencyInfo depInfo{}; + depInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + depInfo.imageMemoryBarrierCount = 1; + depInfo.pImageMemoryBarriers = &barrier; + + vkCmdPipelineBarrier2(commandBuffer, &depInfo); + + VKCHECK(vkEndCommandBuffer(commandBuffer)); + } + + // submit + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + VKCHECK(vkQueueSubmit(pimpl->device.queues.transferQueues[0], 1, &submitInfo, VK_NULL_HANDLE)); + + VKCHECK(vkQueueWaitIdle(pimpl->device.queues.transferQueues[0])); + + vkFreeCommandBuffers(pimpl->device.device, pimpl->transferCommandPool, 1, &commandBuffer); + + return out; } - gfx::Texture* GFXDevice::createTexture( + void GFXDevice::destroyImage(const gfx::Image *image) + { + vkDestroyImageView(pimpl->device.device, image->view, nullptr); + vmaDestroyImage(pimpl->allocator, image->image, image->allocation); + delete image; + } + + gfx::Sampler *GFXDevice::createSampler() + { + gfx::Sampler* out = new gfx::Sampler{}; + + VkSamplerCreateInfo samplerInfo{}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_NEAREST; + samplerInfo.minFilter = VK_FILTER_NEAREST; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.mipLodBias = 0.0f; // wtf + samplerInfo.anisotropyEnable = VK_FALSE; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 0.0f; + + VKCHECK(vkCreateSampler(pimpl->device.device, &samplerInfo, nullptr, &out->sampler)); + + return out; + } + + void GFXDevice::destroySampler(const gfx::Sampler *sampler) + { + vkDestroySampler(pimpl->device.device, sampler->sampler, nullptr); + delete sampler; + } + + gfx::Texture* GFXDevice::createTexture( const void* imageData, uint32_t width, uint32_t height,