mirror of
https://github.com/love2d/love.git
synced 2026-08-12 16:40:57 +02:00
vulkan: implement setRenderTargetsInternal
basic usage of canvases now works. Colors probably aren't correct yet.
This commit is contained in:
@@ -200,6 +200,11 @@ namespace love {
|
||||
Shader::current = Shader::standardShaders[graphics::Shader::StandardShader::STANDARD_DEFAULT];
|
||||
currentPolygonMode = VK_POLYGON_MODE_FILL;
|
||||
restoreState(states.back());
|
||||
|
||||
setViewportSize(width, height, pixelwidth, pixelheight);
|
||||
renderTargetTexture = nullptr;
|
||||
currentViewportWidth = 0.0f;
|
||||
currentViewportHeight = 0.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -317,6 +322,24 @@ namespace love {
|
||||
vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(cmd.indexCount), static_cast<uint32_t>(cmd.instanceCount), 0, 0, 0);
|
||||
}
|
||||
|
||||
void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) {
|
||||
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
|
||||
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
|
||||
|
||||
prepareDraw(attributes, buffers, texture, PRIMITIVE_TRIANGLES, CULL_BACK);
|
||||
|
||||
vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)quadIndexBuffer->getHandle(), 0, getVulkanIndexBufferType(INDEX_UINT16));
|
||||
|
||||
int baseVertex = start * 4;
|
||||
|
||||
for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) {
|
||||
int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
|
||||
|
||||
vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(quadcount * 6), 1, 0, baseVertex, 0);
|
||||
baseVertex += quadcount * 4;
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::setColor(Colorf c) {
|
||||
c.r = std::min(std::max(c.r, 0.0f), 1.0f);
|
||||
c.g = std::min(std::max(c.g, 0.0f), 1.0f);
|
||||
@@ -363,24 +386,6 @@ namespace love {
|
||||
return RENDERER_VULKAN;
|
||||
}
|
||||
|
||||
void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) {
|
||||
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
|
||||
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
|
||||
|
||||
prepareDraw(attributes, buffers, texture, PRIMITIVE_TRIANGLES, CULL_BACK);
|
||||
|
||||
vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)quadIndexBuffer->getHandle(), 0, getVulkanIndexBufferType(INDEX_UINT16));
|
||||
|
||||
int baseVertex = start * 4;
|
||||
|
||||
for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) {
|
||||
int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
|
||||
|
||||
vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(quadcount * 6), 1, 0, baseVertex, 0);
|
||||
baseVertex += quadcount * 4;
|
||||
}
|
||||
}
|
||||
|
||||
graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) {
|
||||
return new StreamBuffer(this, type, size);
|
||||
}
|
||||
@@ -391,6 +396,14 @@ namespace love {
|
||||
}
|
||||
|
||||
void Graphics::setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) {
|
||||
endRenderPass();
|
||||
|
||||
if (rts.colors.size() == 0) {
|
||||
startRenderPass(nullptr, swapChainExtent.width, swapChainExtent.height);
|
||||
} else {
|
||||
auto firstRenderTarget = rts.getFirstTarget();
|
||||
startRenderPass(static_cast<Texture*>(firstRenderTarget.texture), pixelw, pixelh);
|
||||
}
|
||||
}
|
||||
|
||||
// END IMPLEMENTATION OVERRIDDEN FUNCTIONS
|
||||
@@ -420,34 +433,17 @@ namespace love {
|
||||
throw love::Exception("failed to begin recording command buffer");
|
||||
}
|
||||
|
||||
VkRenderingAttachmentInfo colorAttachmentInfo{};
|
||||
colorAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
||||
colorAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
||||
colorAttachmentInfo.imageView = swapChainImageViews[imageIndex];
|
||||
colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
colorAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorAttachmentInfo.clearValue = clearColor;
|
||||
|
||||
VkRenderingInfo renderingInfo{};
|
||||
renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
renderingInfo.renderArea.extent = swapChainExtent;
|
||||
renderingInfo.layerCount = 1;
|
||||
renderingInfo.colorAttachmentCount = 1;
|
||||
renderingInfo.pColorAttachments = &colorAttachmentInfo;
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
vkCmdBeginRendering(commandBuffers.at(imageIndex), &renderingInfo);
|
||||
|
||||
currentGraphicsPipeline = VK_NULL_HANDLE;
|
||||
startRenderPass(nullptr, swapChainExtent.width, swapChainExtent.height);
|
||||
}
|
||||
|
||||
void Graphics::endRecordingGraphicsCommands() {
|
||||
const auto& commandBuffer = commandBuffers.at(imageIndex);
|
||||
|
||||
vkCmdEndRendering(commandBuffer);
|
||||
endRenderPass();
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
|
||||
if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to record command buffer");
|
||||
@@ -1202,6 +1198,9 @@ namespace love {
|
||||
configuration.blendState = states.back().blend;
|
||||
configuration.winding = states.back().winding;
|
||||
configuration.cullmode = cullmode;
|
||||
configuration.framebufferFormat = currentFramebufferOutputFormat;
|
||||
configuration.viewportWidth = currentViewportWidth;
|
||||
configuration.viewportHeight = currentViewportHeight;
|
||||
std::vector<VkBuffer> bufferVector;
|
||||
|
||||
std::vector<VkDeviceSize> offsets;
|
||||
@@ -1234,6 +1233,56 @@ namespace love {
|
||||
vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, static_cast<uint32_t>(bufferVector.size()), bufferVector.data(), offsets.data());
|
||||
}
|
||||
|
||||
void Graphics::startRenderPass(Texture* texture, uint32_t w, uint32_t h) {
|
||||
VkRenderingAttachmentInfo colorAttachmentInfo{};
|
||||
colorAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
||||
colorAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
if (texture) {
|
||||
colorAttachmentInfo.imageView = texture->getImageView();
|
||||
colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // fixme: we want to clear a canvas sometimes.
|
||||
auto vulkanFormat = Vulkan::getTextureFormat(texture->getPixelFormat());
|
||||
currentFramebufferOutputFormat = vulkanFormat.internalFormat;
|
||||
|
||||
renderTargetTexture = texture;
|
||||
} else {
|
||||
colorAttachmentInfo.imageView = swapChainImageViews[imageIndex];
|
||||
colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
currentFramebufferOutputFormat = swapChainImageFormat;
|
||||
|
||||
renderTargetTexture = nullptr;
|
||||
}
|
||||
colorAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorAttachmentInfo.clearValue = clearColor;
|
||||
|
||||
VkRenderingInfo renderingInfo{};
|
||||
renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
renderingInfo.renderArea.extent.width = w;
|
||||
renderingInfo.renderArea.extent.height = h;
|
||||
renderingInfo.layerCount = 1;
|
||||
renderingInfo.colorAttachmentCount = 1;
|
||||
renderingInfo.pColorAttachments = &colorAttachmentInfo;
|
||||
|
||||
currentViewportWidth = (float)w;
|
||||
currentViewportHeight = (float)h;
|
||||
|
||||
if (renderTargetTexture) {
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), (VkImage)texture->getHandle(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
}
|
||||
|
||||
vkCmdBeginRendering(commandBuffers.at(imageIndex), &renderingInfo);
|
||||
|
||||
currentGraphicsPipeline = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void Graphics::endRenderPass() {
|
||||
vkCmdEndRendering(commandBuffers.at(imageIndex));
|
||||
|
||||
if (renderTargetTexture) {
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), (VkImage)renderTargetTexture->getHandle(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
|
||||
renderTargetTexture = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) {
|
||||
auto shader = configuration.shader;
|
||||
auto shaderStages = shader->getShaderStages();
|
||||
@@ -1253,8 +1302,8 @@ namespace love {
|
||||
VkViewport viewport{};
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = (float)swapChainExtent.width;
|
||||
viewport.height = (float)swapChainExtent.height;
|
||||
viewport.width = configuration.viewportWidth;
|
||||
viewport.height = configuration.viewportHeight;
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
|
||||
@@ -1323,10 +1372,12 @@ namespace love {
|
||||
}
|
||||
graphicsPipelineLayouts.push_back(pipelineLayout);
|
||||
|
||||
VkFormat framebufferOutputFormat = configuration.framebufferFormat;
|
||||
|
||||
VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{};
|
||||
pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
|
||||
pipelineRenderingCreateInfo.colorAttachmentCount = 1;
|
||||
pipelineRenderingCreateInfo.pColorAttachmentFormats = &swapChainImageFormat;
|
||||
pipelineRenderingCreateInfo.pColorAttachmentFormats = &framebufferOutputFormat;
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineInfo{};
|
||||
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
@@ -1440,6 +1491,15 @@ namespace love {
|
||||
}
|
||||
|
||||
bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other) {
|
||||
if (first.viewportHeight != other.viewportHeight) {
|
||||
return false;
|
||||
}
|
||||
if (first.viewportWidth != other.viewportWidth) {
|
||||
return false;
|
||||
}
|
||||
if (first.framebufferFormat != other.framebufferFormat) {
|
||||
return false;
|
||||
}
|
||||
if (first.cullmode != other.cullmode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@ namespace love {
|
||||
ColorChannelMask colorChannelMask;
|
||||
Winding winding;
|
||||
CullMode cullmode;
|
||||
VkFormat framebufferFormat;
|
||||
float viewportWidth;
|
||||
float viewportHeight;
|
||||
|
||||
friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
|
||||
};
|
||||
@@ -174,6 +177,8 @@ namespace love {
|
||||
graphics::StreamBuffer* getUniformBuffer();
|
||||
void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration);
|
||||
void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
|
||||
void startRenderPass(Texture*, uint32_t w, uint32_t h);
|
||||
void endRenderPass();
|
||||
|
||||
VkInstance instance = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||
@@ -213,7 +218,10 @@ namespace love {
|
||||
std::vector<std::pair<DecriptorSetConfiguration, std::vector<VkDescriptorSet>>> descriptorSetsMap;
|
||||
VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
|
||||
|
||||
VkCommandBuffer offscreenRendertargetCommandBuffer;
|
||||
VkFormat currentFramebufferOutputFormat = VK_FORMAT_UNDEFINED;
|
||||
Texture* renderTargetTexture = nullptr;
|
||||
float currentViewportWidth = 0;
|
||||
float currentViewportHeight = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,12 @@ namespace love {
|
||||
|
||||
auto vulkanFormat = Vulkan::getTextureFormat(format);
|
||||
|
||||
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
|
||||
if (isRenderTarget()) {
|
||||
usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
}
|
||||
|
||||
VkImageCreateInfo imageInfo{};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
@@ -30,7 +36,7 @@ namespace love {
|
||||
imageInfo.format = vulkanFormat.internalFormat;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
imageInfo.usage = usageFlags;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
@@ -39,7 +45,6 @@ namespace love {
|
||||
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to create image");
|
||||
}
|
||||
// fixme: is there a way we don't use the general image layout?
|
||||
transitionImageLayout(textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
|
||||
|
||||
if (data) {
|
||||
@@ -62,7 +67,7 @@ namespace love {
|
||||
defaultPixels.push_back(255);
|
||||
defaultPixels.push_back(255);
|
||||
defaultPixels.push_back(255);
|
||||
defaultPixels.push_back(0);
|
||||
defaultPixels.push_back(255);
|
||||
}
|
||||
Rect rect = { 0, 0, width, height };
|
||||
uploadByteData(PIXELFORMAT_RGBA8_UNORM, defaultPixels.data(), defaultPixels.size(), 0, 0, rect);
|
||||
@@ -174,7 +179,8 @@ namespace love {
|
||||
static_cast<uint32_t>(r.w),
|
||||
static_cast<uint32_t>(r.h), 1
|
||||
};
|
||||
|
||||
|
||||
// fixme: we should use VK_IMAGE_LAYOUT_DST_OPTIMAL for transfer
|
||||
vkCmdCopyBufferToImage(
|
||||
commandBuffer,
|
||||
buffer,
|
||||
|
||||
@@ -279,8 +279,7 @@ namespace love {
|
||||
std::string Vulkan::getVulkanApiVersion(uint32_t version) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << VK_API_VERSION_VARIANT(version)
|
||||
<< "." << VK_API_VERSION_MAJOR(version)
|
||||
ss << "." << VK_API_VERSION_MAJOR(version)
|
||||
<< "." << VK_API_VERSION_MINOR(version)
|
||||
<< "." << VK_API_VERSION_PATCH(version);
|
||||
|
||||
@@ -458,6 +457,20 @@ namespace love {
|
||||
sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
}
|
||||
else if (oldLayout == VK_IMAGE_LAYOUT_GENERAL && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
|
||||
barrier.srcAccessMask = 0;
|
||||
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
sourceStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
}
|
||||
else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_GENERAL) {
|
||||
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
barrier.dstAccessMask = 0;
|
||||
|
||||
sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
}
|
||||
else {
|
||||
throw std::invalid_argument("unsupported layout transition!");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user