Browse Source

vulkan: use dynamic rendering

This vulkan 1.3 feature will make the code simpler when dealing
with off screen render targets (i.e. löve canvases)
niki 3 years ago
parent
commit
0ae81091d1

+ 1 - 1
src/modules/graphics/vulkan/Buffer.cpp

@@ -25,7 +25,7 @@ namespace love {
 				return vkFlags;
 				return vkFlags;
 			}
 			}
 
 
-			Buffer::Buffer(VmaAllocator allocator, love::graphics::Graphics* gfx, const Settings& settings, const std::vector<DataDeclaration>& format, const void* data, size_t size, size_t arraylength)
+			Buffer::Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector<DataDeclaration>& format, const void* data, size_t size, size_t arraylength)
 				: love::graphics::Buffer(gfx, settings, format, size, arraylength), usageFlags(settings.usageFlags), allocator(allocator), gfx(gfx) {
 				: love::graphics::Buffer(gfx, settings, format, size, arraylength), usageFlags(settings.usageFlags), allocator(allocator), gfx(gfx) {
 				loadVolatile();
 				loadVolatile();
 			}
 			}

+ 1 - 1
src/modules/graphics/vulkan/Buffer.h

@@ -9,7 +9,7 @@ namespace love {
 		namespace vulkan {
 		namespace vulkan {
 			class Buffer : public love::graphics::Buffer, public Volatile {
 			class Buffer : public love::graphics::Buffer, public Volatile {
 			public:
 			public:
-				Buffer(VmaAllocator allocator, love::graphics::Graphics* gfx, const Settings& settings, const std::vector<DataDeclaration>& format, const void* data, size_t size, size_t arraylength);
+				Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector<DataDeclaration>& format, const void* data, size_t size, size_t arraylength);
 				virtual ~Buffer();
 				virtual ~Buffer();
 
 
 				virtual bool loadVolatile() override;
 				virtual bool loadVolatile() override;

+ 46 - 127
src/modules/graphics/vulkan/Graphics.cpp

@@ -38,12 +38,14 @@ namespace love {
 			};
 			};
 
 
 #ifdef NDEBUG
 #ifdef NDEBUG
-			const bool enableValidationLayers = false;
+			constexpr bool enableValidationLayers = false;
 #else
 #else
-			const bool enableValidationLayers = true;
+			constexpr bool enableValidationLayers = true;
 #endif
 #endif
 
 
-			const int MAX_FRAMES_IN_FLIGHT = 2;
+			constexpr int MAX_FRAMES_IN_FLIGHT = 2;
+
+			constexpr uint32_t vulkanApiVersion = VK_API_VERSION_1_3;
 
 
 			const char* Graphics::getName() const {
 			const char* Graphics::getName() const {
 				return "love.graphics.vulkan";
 				return "love.graphics.vulkan";
@@ -77,8 +79,7 @@ namespace love {
 			}
 			}
 
 
 			love::graphics::Buffer* Graphics::newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector<love::graphics::Buffer::DataDeclaration>& format, const void* data, size_t size, size_t arraylength) {
 			love::graphics::Buffer* Graphics::newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector<love::graphics::Buffer::DataDeclaration>& format, const void* data, size_t size, size_t arraylength) {
-				std::cout << "newBuffer ";
-				return new Buffer(vmaAllocator, this, settings, format, data, size, arraylength);
+				return new Buffer(this, settings, format, data, size, arraylength);
 			}
 			}
 
 
 			void Graphics::present(void* screenshotCallbackdata) {
 			void Graphics::present(void* screenshotCallbackdata) {
@@ -135,8 +136,6 @@ namespace love {
 					throw love::Exception("failed to present swap chain image");
 					throw love::Exception("failed to present swap chain image");
 				}
 				}
 				
 				
-				std::cout << "present" << std::endl;
-
 				currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
 				currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
 
 
 				updatedBatchedDrawBuffers();
 				updatedBatchedDrawBuffers();
@@ -144,7 +143,6 @@ namespace love {
 			}
 			}
 
 
 			void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) {
 			void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) {
-				std::cout << "setViewPortSize ";
 				this->width = width;
 				this->width = width;
 				this->height = height;
 				this->height = height;
 				this->pixelWidth = pixelwidth;
 				this->pixelWidth = pixelwidth;
@@ -156,8 +154,6 @@ namespace love {
 			}
 			}
 
 
 			bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) {
 			bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) {
-				std::cout << "setMode ";
-
 				createVulkanInstance();
 				createVulkanInstance();
 				createSurface();
 				createSurface();
 				pickPhysicalDevice();
 				pickPhysicalDevice();
@@ -166,10 +162,8 @@ namespace love {
 				initCapabilities();
 				initCapabilities();
 				createSwapChain();
 				createSwapChain();
 				createImageViews();
 				createImageViews();
-				createRenderPass();
 				createDefaultShaders();
 				createDefaultShaders();
 				createDescriptorSetLayout();
 				createDescriptorSetLayout();
-				createFramebuffers();
 				createCommandPool();
 				createCommandPool();
 				createCommandBuffers();
 				createCommandBuffers();
 				createDefaultTexture();
 				createDefaultTexture();
@@ -211,8 +205,6 @@ namespace love {
 			}
 			}
 
 
 			void Graphics::initCapabilities() {
 			void Graphics::initCapabilities() {
-				std::cout << "initCapabilities ";
-
 				// todo
 				// todo
 				capabilities.features[FEATURE_MULTI_RENDER_TARGET_FORMATS] = false;
 				capabilities.features[FEATURE_MULTI_RENDER_TARGET_FORMATS] = false;
 				capabilities.features[FEATURE_CLAMP_ZERO] = false;
 				capabilities.features[FEATURE_CLAMP_ZERO] = false;
@@ -258,8 +250,6 @@ namespace love {
 			}
 			}
 
 
 			void Graphics::unSetMode() {
 			void Graphics::unSetMode() {
-				std::cout << "unSetMode ";
-				
 				created = false;
 				created = false;
 				vkDeviceWaitIdle(device);
 				vkDeviceWaitIdle(device);
 				Volatile::unloadAll();
 				Volatile::unloadAll();
@@ -291,8 +281,6 @@ namespace love {
 			}
 			}
 
 
 			void Graphics::setPointSize(float size) {
 			void Graphics::setPointSize(float size) {
-				std::cout << "setPointSize ";
-
 				if (size != states.back().pointSize)
 				if (size != states.back().pointSize)
 					flushBatchedDraws();
 					flushBatchedDraws();
 
 
@@ -300,8 +288,6 @@ namespace love {
 			}
 			}
 
 
 			bool Graphics::usesGLSLES() const {
 			bool Graphics::usesGLSLES() const {
-				std::cout << "usesGLSLES ";
-
 				return false;
 				return false;
 			}
 			}
 			
 			
@@ -319,16 +305,12 @@ namespace love {
 			}
 			}
 
 
 			void Graphics::draw(const DrawCommand& cmd) {
 			void Graphics::draw(const DrawCommand& cmd) {
-				std::cout << "draw ";
-
 				prepareDraw(*cmd.attributes, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode);
 				prepareDraw(*cmd.attributes, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode);
 
 
 				vkCmdDraw(commandBuffers.at(imageIndex), static_cast<uint32_t>(cmd.vertexCount), static_cast<uint32_t>(cmd.instanceCount), static_cast<uint32_t>(cmd.vertexStart), 0);
 				vkCmdDraw(commandBuffers.at(imageIndex), static_cast<uint32_t>(cmd.vertexCount), static_cast<uint32_t>(cmd.instanceCount), static_cast<uint32_t>(cmd.vertexStart), 0);
 			}
 			}
 
 
 			void Graphics::draw(const DrawIndexedCommand& cmd) {
 			void Graphics::draw(const DrawIndexedCommand& cmd) {
-				std::cout << "drawIndexed ";
-
 				prepareDraw(*cmd.attributes, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode);
 				prepareDraw(*cmd.attributes, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode);
 
 
 				vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)cmd.indexBuffer->getHandle(), static_cast<VkDeviceSize>(cmd.indexBufferOffset), getVulkanIndexBufferType(cmd.indexType));
 				vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)cmd.indexBuffer->getHandle(), static_cast<VkDeviceSize>(cmd.indexBufferOffset), getVulkanIndexBufferType(cmd.indexType));
@@ -336,8 +318,6 @@ namespace love {
 			}
 			}
 
 
 			void Graphics::setColor(Colorf c) {
 			void Graphics::setColor(Colorf c) {
-				std::cout << "setColor ";
-
 				c.r = std::min(std::max(c.r, 0.0f), 1.0f);
 				c.r = std::min(std::max(c.r, 0.0f), 1.0f);
 				c.g = std::min(std::max(c.g, 0.0f), 1.0f);
 				c.g = std::min(std::max(c.g, 0.0f), 1.0f);
 				c.b = std::min(std::max(c.b, 0.0f), 1.0f);
 				c.b = std::min(std::max(c.b, 0.0f), 1.0f);
@@ -347,8 +327,6 @@ namespace love {
 			}
 			}
 
 
 			void Graphics::setWireframe(bool enable) {
 			void Graphics::setWireframe(bool enable) {
-				std::cout << "setWireframe ";
-
 				flushBatchedDraws();
 				flushBatchedDraws();
 
 
 				if (enable) {
 				if (enable) {
@@ -362,8 +340,6 @@ namespace love {
 			}
 			}
 
 
 			PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const { 
 			PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const { 
-				std::cout << "getSizedFormat ";
-				
 				switch (format) {
 				switch (format) {
 				PIXELFORMAT_NORMAL:
 				PIXELFORMAT_NORMAL:
 					if (isGammaCorrect()) {
 					if (isGammaCorrect()) {
@@ -380,20 +356,14 @@ namespace love {
 			}
 			}
 
 
 			bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) { 
 			bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) { 
-				std::cout << "isPixelFormatSupported ";
-
 				return true;
 				return true;
 			}
 			}
 
 
 			Renderer Graphics::getRenderer() const {
 			Renderer Graphics::getRenderer() const {
-				std::cout << "getRenderer ";
-
 				return RENDERER_VULKAN;
 				return RENDERER_VULKAN;
 			}
 			}
 
 
 			void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) {
 			void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) {
-				std::cout << "drawQuads ";
-
 				const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
 				const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
 				const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
 				const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
 
 
@@ -412,7 +382,6 @@ namespace love {
 			}
 			}
 
 
 			graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) {
 			graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) {
-				std::cout << "newStreamBuffer ";
 				return new StreamBuffer(this, type, size);
 				return new StreamBuffer(this, type, size);
 			}
 			}
 
 
@@ -420,6 +389,9 @@ namespace love {
 				uint32 flags = DEVICE_PROJECTION_DEFAULT;
 				uint32 flags = DEVICE_PROJECTION_DEFAULT;
 				return calculateDeviceProjection(projection, flags);
 				return calculateDeviceProjection(projection, flags);
 			}
 			}
+			
+			void Graphics::setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) {
+			}
 
 
 			// END IMPLEMENTATION OVERRIDDEN FUNCTIONS
 			// END IMPLEMENTATION OVERRIDDEN FUNCTIONS
 
 
@@ -444,29 +416,39 @@ namespace love {
 				beginInfo.flags = 0;
 				beginInfo.flags = 0;
 				beginInfo.pInheritanceInfo = nullptr;
 				beginInfo.pInheritanceInfo = nullptr;
 
 
-				std::cout << "beginCommandBuffer(imageIndex=" << imageIndex << ") ";
 				if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) {
 				if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) {
 					throw love::Exception("failed to begin recording command buffer");
 					throw love::Exception("failed to begin recording command buffer");
 				}
 				}
 
 
-				VkRenderPassBeginInfo renderPassInfo{};
-				renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
-				renderPassInfo.renderPass = renderPass;
-				renderPassInfo.framebuffer = swapChainFramBuffers.at(imageIndex);
-				renderPassInfo.renderArea.offset = { 0, 0 };
-				renderPassInfo.renderArea.extent = swapChainExtent;
-				renderPassInfo.clearValueCount = 1;
-				renderPassInfo.pClearValues = &clearColor;
+				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);
 
 
-				vkCmdBeginRenderPass(commandBuffers.at(imageIndex), &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
 				currentGraphicsPipeline = VK_NULL_HANDLE;
 				currentGraphicsPipeline = VK_NULL_HANDLE;
 			}
 			}
 
 
 			void Graphics::endRecordingGraphicsCommands() {
 			void Graphics::endRecordingGraphicsCommands() {
 				const auto& commandBuffer = commandBuffers.at(imageIndex);
 				const auto& commandBuffer = commandBuffers.at(imageIndex);
 
 
-				std::cout << "endCommandBuffer(imageIndex=" << imageIndex << ") ";
-				vkCmdEndRenderPass(commandBuffers.at(imageIndex));
+				vkCmdEndRendering(commandBuffer);
+
+				Vulkan::cmdTransitionImageLayout(commandBuffer, swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+
 				if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) {
 				if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) {
 					throw love::Exception("failed to record command buffer");
 					throw love::Exception("failed to record command buffer");
 				}
 				}
@@ -601,7 +583,7 @@ namespace love {
 				appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);	//todo, get this version from somewhere else?
 				appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);	//todo, get this version from somewhere else?
 				appInfo.pEngineName = "LOVE Engine";
 				appInfo.pEngineName = "LOVE Engine";
 				appInfo.engineVersion = VK_MAKE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_REV);
 				appInfo.engineVersion = VK_MAKE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_REV);
-				appInfo.apiVersion = VK_API_VERSION_1_0;
+				appInfo.apiVersion = vulkanApiVersion;
 
 
 				VkInstanceCreateInfo createInfo{};
 				VkInstanceCreateInfo createInfo{};
 				createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
 				createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
@@ -809,6 +791,10 @@ namespace love {
 					queueCreateInfos.push_back(queueCreateInfo);
 					queueCreateInfos.push_back(queueCreateInfo);
 				}
 				}
 
 
+				VkPhysicalDeviceDynamicRenderingFeatures dynamicRenderingFeature{};
+				dynamicRenderingFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES;
+				dynamicRenderingFeature.dynamicRendering = VK_TRUE;
+
 				VkPhysicalDeviceFeatures deviceFeatures{};
 				VkPhysicalDeviceFeatures deviceFeatures{};
 				deviceFeatures.samplerAnisotropy = VK_TRUE;
 				deviceFeatures.samplerAnisotropy = VK_TRUE;
 
 
@@ -817,7 +803,7 @@ namespace love {
 				createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
 				createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
 				createInfo.pQueueCreateInfos = queueCreateInfos.data();
 				createInfo.pQueueCreateInfos = queueCreateInfos.data();
 				createInfo.pEnabledFeatures = &deviceFeatures;
 				createInfo.pEnabledFeatures = &deviceFeatures;
-				createInfo.pNext = nullptr;
+				createInfo.pNext = &dynamicRenderingFeature;
 
 
 				createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
 				createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
 				createInfo.ppEnabledExtensionNames = deviceExtensions.data();
 				createInfo.ppEnabledExtensionNames = deviceExtensions.data();
@@ -845,7 +831,7 @@ namespace love {
 				vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;
 				vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;
 
 
 				VmaAllocatorCreateInfo allocatorCreateInfo = {};
 				VmaAllocatorCreateInfo allocatorCreateInfo = {};
-				allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_0;
+				allocatorCreateInfo.vulkanApiVersion = vulkanApiVersion;
 				allocatorCreateInfo.physicalDevice = physicalDevice;
 				allocatorCreateInfo.physicalDevice = physicalDevice;
 				allocatorCreateInfo.device = device;
 				allocatorCreateInfo.device = device;
 				allocatorCreateInfo.instance = instance;
 				allocatorCreateInfo.instance = instance;
@@ -1013,48 +999,6 @@ namespace love {
 				}
 				}
 			}
 			}
 
 
-			void Graphics::createRenderPass() {
-				VkAttachmentDescription colorAttachment{};
-				colorAttachment.format = swapChainImageFormat;
-				colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
-				colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
-				colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-				colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-				colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-				colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-				colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
-
-				VkAttachmentReference colorAttachmentRef{};
-				colorAttachmentRef.attachment = 0;
-				colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-				VkSubpassDescription subpass{};
-				subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
-				subpass.colorAttachmentCount = 1;
-				subpass.pColorAttachments = &colorAttachmentRef;
-
-				VkSubpassDependency dependency{};
-				dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
-				dependency.dstSubpass = 0;
-				dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-				dependency.srcAccessMask = 0;
-				dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-				dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-
-				VkRenderPassCreateInfo renderPassInfo{};
-				renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
-				renderPassInfo.attachmentCount = 1;
-				renderPassInfo.pAttachments = &colorAttachment;
-				renderPassInfo.subpassCount = 1;
-				renderPassInfo.pSubpasses = &subpass;
-				renderPassInfo.dependencyCount = 1;
-				renderPassInfo.pDependencies = &dependency;
-
-				if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
-					throw love::Exception("failed to create render pass");
-				}
-			}
-
 			void Graphics::createDefaultShaders() {
 			void Graphics::createDefaultShaders() {
 				for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) {
 				for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) {
 					auto stype = (Shader::StandardShader)i;
 					auto stype = (Shader::StandardShader)i;
@@ -1379,6 +1323,11 @@ namespace love {
 				}
 				}
 				graphicsPipelineLayouts.push_back(pipelineLayout);
 				graphicsPipelineLayouts.push_back(pipelineLayout);
 
 
+				VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{};
+				pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
+				pipelineRenderingCreateInfo.colorAttachmentCount = 1;
+				pipelineRenderingCreateInfo.pColorAttachmentFormats = &swapChainImageFormat;
+
 				VkGraphicsPipelineCreateInfo pipelineInfo{};
 				VkGraphicsPipelineCreateInfo pipelineInfo{};
 				pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
 				pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
 				pipelineInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
 				pipelineInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
@@ -1392,10 +1341,10 @@ namespace love {
 				pipelineInfo.pColorBlendState = &colorBlending;
 				pipelineInfo.pColorBlendState = &colorBlending;
 				pipelineInfo.pDynamicState = nullptr;
 				pipelineInfo.pDynamicState = nullptr;
 				pipelineInfo.layout = pipelineLayout;
 				pipelineInfo.layout = pipelineLayout;
-				pipelineInfo.renderPass = renderPass;
 				pipelineInfo.subpass = 0;
 				pipelineInfo.subpass = 0;
 				pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
 				pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
 				pipelineInfo.basePipelineIndex = -1;
 				pipelineInfo.basePipelineIndex = -1;
+				pipelineInfo.pNext = &pipelineRenderingCreateInfo;
 
 
 				VkPipeline graphicsPipeline;
 				VkPipeline graphicsPipeline;
 				if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
 				if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
@@ -1425,35 +1374,13 @@ namespace love {
 				}
 				}
 			}
 			}
 
 
-			void Graphics::createFramebuffers() {
-				swapChainFramBuffers.resize(swapChainImageViews.size());
-				for (size_t i = 0; i < swapChainImageViews.size(); i++) {
-					VkImageView attachments[] = {
-						swapChainImageViews.at(i)
-					};
-
-					VkFramebufferCreateInfo framebufferInfo{};
-					framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
-					framebufferInfo.renderPass = renderPass;
-					framebufferInfo.attachmentCount = 1;
-					framebufferInfo.pAttachments = attachments;
-					framebufferInfo.width = swapChainExtent.width;
-					framebufferInfo.height = swapChainExtent.height;
-					framebufferInfo.layers = 1;
-
-					if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramBuffers.at(i)) != VK_SUCCESS) {
-						throw love::Exception("failed to create framebuffers");
-					}
-				}
-			}
-
 			void Graphics::createCommandPool() {
 			void Graphics::createCommandPool() {
 				QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
 				QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
 
 
 				VkCommandPoolCreateInfo poolInfo{};
 				VkCommandPoolCreateInfo poolInfo{};
 				poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
 				poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
 				poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
 				poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
-				poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+				poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
 
 
 				if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
 				if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
 					throw love::Exception("failed to create command pool");
 					throw love::Exception("failed to create command pool");
@@ -1461,7 +1388,7 @@ namespace love {
 			}
 			}
 
 
 			void Graphics::createCommandBuffers() {
 			void Graphics::createCommandBuffers() {
-				commandBuffers.resize(swapChainFramBuffers.size());
+				commandBuffers.resize(swapChainImages.size());
 
 
 				VkCommandBufferAllocateInfo allocInfo{};
 				VkCommandBufferAllocateInfo allocInfo{};
 				allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
 				allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
@@ -1591,12 +1518,7 @@ namespace love {
 			}
 			}
 
 
 			void Graphics::cleanupSwapChain() {
 			void Graphics::cleanupSwapChain() {
-				std::cout << "cleanupSwapChain ";
-
 				vkDestroyDescriptorPool(device, descriptorPool, nullptr);
 				vkDestroyDescriptorPool(device, descriptorPool, nullptr);
-				for (size_t i = 0; i < swapChainFramBuffers.size(); i++) {
-					vkDestroyFramebuffer(device, swapChainFramBuffers[i], nullptr);
-				}
 				vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
 				vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
 				for (auto const& p : graphicsPipelines) {
 				for (auto const& p : graphicsPipelines) {
 					vkDestroyPipeline(device, p.second, nullptr);
 					vkDestroyPipeline(device, p.second, nullptr);
@@ -1607,7 +1529,6 @@ namespace love {
 					vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
 					vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
 				}
 				}
 				graphicsPipelineLayouts.clear();
 				graphicsPipelineLayouts.clear();
-				vkDestroyRenderPass(device, renderPass, nullptr);
 				for (size_t i = 0; i < swapChainImageViews.size(); i++) {
 				for (size_t i = 0; i < swapChainImageViews.size(); i++) {
 					vkDestroyImageView(device, swapChainImageViews[i], nullptr);
 					vkDestroyImageView(device, swapChainImageViews[i], nullptr);
 				}
 				}
@@ -1626,8 +1547,6 @@ namespace love {
 
 
 				createSwapChain();
 				createSwapChain();
 				createImageViews();
 				createImageViews();
-				createRenderPass();
-				createFramebuffers();
 				createDescriptorPool();
 				createDescriptorPool();
 				createCommandBuffers();
 				createCommandBuffers();
 				startRecordingGraphicsCommands();
 				startRecordingGraphicsCommands();

+ 15 - 19
src/modules/graphics/vulkan/Graphics.h

@@ -85,22 +85,22 @@ namespace love {
 				// implementation for virtual functions
 				// implementation for virtual functions
 				love::graphics::Texture* newTexture(const love::graphics::Texture::Settings& settings, const love::graphics::Texture::Slices* data = nullptr) override;
 				love::graphics::Texture* newTexture(const love::graphics::Texture::Settings& settings, const love::graphics::Texture::Slices* data = nullptr) override;
 				love::graphics::Buffer* newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector<love::graphics::Buffer::DataDeclaration>& format, const void* data, size_t size, size_t arraylength) override;
 				love::graphics::Buffer* newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector<love::graphics::Buffer::DataDeclaration>& format, const void* data, size_t size, size_t arraylength) override;
-				void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override { std::cout << "clear1 "; }
-				void clear(const std::vector<OptionalColorD>& colors, OptionalInt stencil, OptionalDouble depth) override { std::cout << "clear2 "; }
+				void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override { }
+				void clear(const std::vector<OptionalColorD>& colors, OptionalInt stencil, OptionalDouble depth) override { }
 				Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override;
 				Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override;
-				void discard(const std::vector<bool>& colorbuffers, bool depthstencil) override { std::cout << "discard "; }
+				void discard(const std::vector<bool>& colorbuffers, bool depthstencil) override { }
 				void present(void* screenshotCallbackdata) override;
 				void present(void* screenshotCallbackdata) override;
 				void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
 				void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
 				bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
 				bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
 				void unSetMode() override;
 				void unSetMode() override;
-				void setActive(bool active) override { std::cout << "setActive "; }
-				int getRequestedBackbufferMSAA() const override { std::cout << "getRequestedBackbufferMSAA "; return 0; }
-				int getBackbufferMSAA() const  override { std::cout << "getBackbufferMSAA "; return 0; }
+				void setActive(bool active) override { }
+				int getRequestedBackbufferMSAA() const override { return 0; }
+				int getBackbufferMSAA() const  override { return 0; }
 				void setColor(Colorf c) override;
 				void setColor(Colorf c) override;
-				void setScissor(const Rect& rect) override { std::cout << "setScissor "; }
-				void setScissor() override { std::cout << "setScissor2 "; }
-				void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override { std::cout << "setStencilMode "; }
-				void setDepthMode(CompareMode compare, bool write) override { std::cout << "setDepthMode "; }
+				void setScissor(const Rect& rect) override { }
+				void setScissor() override { }
+				void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override { }
+				void setDepthMode(CompareMode compare, bool write) override { }
 				void setFrontFaceWinding(Winding winding) override;
 				void setFrontFaceWinding(Winding winding) override;
 				void setColorMask(ColorChannelMask mask) override;
 				void setColorMask(ColorChannelMask mask) override;
 				void setBlendState(const BlendState& blend) override;
 				void setBlendState(const BlendState& blend) override;
@@ -123,18 +123,16 @@ namespace love {
 
 
 			protected:
 			protected:
 				graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { 
 				graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { 
-					std::cout << "newShaderStageInternal "; 
 					return new ShaderStage(this, stage, source, gles, cachekey); 
 					return new ShaderStage(this, stage, source, gles, cachekey); 
 				}
 				}
 				graphics::Shader* newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override { 
 				graphics::Shader* newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override { 
-					std::cout << "newShaderInternal "; 
 					return new Shader(stages);
 					return new Shader(stages);
 				}
 				}
 				graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override;
 				graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override;
-				bool dispatch(int x, int y, int z) override { std::cout << "dispatch "; return false; }
+				bool dispatch(int x, int y, int z) override { return false; }
 				void initCapabilities() override;
 				void initCapabilities() override;
-				void getAPIStats(int& shaderswitches) const override { std::cout << "getAPIStats "; }
-				void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override { std::cout << "setRenderTargetsInternal "; }
+				void getAPIStats(int& shaderswitches) const override { }
+				void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override;
 
 
 			private:
 			private:
 				void createVulkanInstance();
 				void createVulkanInstance();
@@ -152,13 +150,11 @@ namespace love {
 				VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
 				VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
 				void createSwapChain();
 				void createSwapChain();
 				void createImageViews();
 				void createImageViews();
-				void createRenderPass();
 				void createDefaultShaders();
 				void createDefaultShaders();
 				void createDescriptorSetLayout();
 				void createDescriptorSetLayout();
 				void createDescriptorPool();
 				void createDescriptorPool();
 				std::vector<VkDescriptorSet> createDescriptorSets(DecriptorSetConfiguration);
 				std::vector<VkDescriptorSet> createDescriptorSets(DecriptorSetConfiguration);
 				VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration);
 				VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration);
-				void createFramebuffers();
 				void createCommandPool();
 				void createCommandPool();
 				void createCommandBuffers();
 				void createCommandBuffers();
 				void createSyncObjects();
 				void createSyncObjects();
@@ -192,11 +188,9 @@ namespace love {
 				std::vector<VkImageView> swapChainImageViews;
 				std::vector<VkImageView> swapChainImageViews;
 				VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
 				VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
 				VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
 				VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
-				VkRenderPass renderPass = VK_NULL_HANDLE;
 				VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
 				VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
 				std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines;	// FIXME improve performance by using a hash map
 				std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines;	// FIXME improve performance by using a hash map
 				std::vector<VkPipelineLayout> graphicsPipelineLayouts;
 				std::vector<VkPipelineLayout> graphicsPipelineLayouts;
-				std::vector<VkFramebuffer> swapChainFramBuffers;
 				VkCommandPool commandPool = VK_NULL_HANDLE;
 				VkCommandPool commandPool = VK_NULL_HANDLE;
 				std::vector<VkCommandBuffer> commandBuffers;
 				std::vector<VkCommandBuffer> commandBuffers;
 				VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} };
 				VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} };
@@ -218,6 +212,8 @@ namespace love {
 				std::vector<std::pair<graphics::Shader::BuiltinUniformData, graphics::StreamBuffer*>> uniformBufferMap;
 				std::vector<std::pair<graphics::Shader::BuiltinUniformData, graphics::StreamBuffer*>> uniformBufferMap;
 				std::vector<std::pair<DecriptorSetConfiguration, std::vector<VkDescriptorSet>>> descriptorSetsMap;
 				std::vector<std::pair<DecriptorSetConfiguration, std::vector<VkDescriptorSet>>> descriptorSetsMap;
 				VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
 				VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
+
+				VkCommandBuffer offscreenRendertargetCommandBuffer;
 			};
 			};
 		}
 		}
 	}
 	}

+ 20 - 60
src/modules/graphics/vulkan/Texture.cpp

@@ -53,11 +53,25 @@ namespace love {
 					rect.h = sliceData->getHeight();
 					rect.h = sliceData->getHeight();
 
 
 					uploadByteData(format, dataPtr, size, 0, 0, rect);
 					uploadByteData(format, dataPtr, size, 0, 0, rect);
-				}
-				else {
-					uint8 defaultPixel[] = { 255, 255, 255, 255 };
-					Rect rect = { 0, 0, 1, 1 };
-					uploadByteData(PIXELFORMAT_RGBA8_UNORM, defaultPixel, sizeof(defaultPixel), 0, 0, rect);
+				} else {
+					if (isRenderTarget()) {
+						std::vector<uint8> defaultPixels;
+						defaultPixels.reserve(width * height * 4);
+						for (size_t i = 0; i < width * height; i++) {
+							// transparent white
+							defaultPixels.push_back(255);
+							defaultPixels.push_back(255);
+							defaultPixels.push_back(255);
+							defaultPixels.push_back(0);
+						}
+						Rect rect = { 0, 0, width, height };
+						uploadByteData(PIXELFORMAT_RGBA8_UNORM, defaultPixels.data(), defaultPixels.size(), 0, 0, rect);
+					}
+					else {
+						std::vector<uint8> defaultPixels(width * height * 4, 255);
+						Rect rect = { 0, 0, width, height };
+						uploadByteData(PIXELFORMAT_RGBA8_UNORM, defaultPixels.data(), defaultPixels.size(), 0, 0, rect);
+					}
 				}
 				}
 				createTextureImageView();
 				createTextureImageView();
 				createTextureSampler();
 				createTextureSampler();
@@ -137,61 +151,7 @@ namespace love {
 			void Texture::transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) {
 			void Texture::transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) {
 				auto commandBuffer = vgfx->beginSingleTimeCommands();
 				auto commandBuffer = vgfx->beginSingleTimeCommands();
 
 
-				VkPipelineStageFlags sourceStage;
-				VkPipelineStageFlags destinationStage;
-
-				VkImageMemoryBarrier barrier{};
-				barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
-				barrier.oldLayout = oldLayout;
-				barrier.newLayout = newLayout;
-				barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-				barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-				barrier.image = image;
-				barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-				barrier.subresourceRange.baseMipLevel = 0;
-				barrier.subresourceRange.levelCount = 1;
-				barrier.subresourceRange.baseArrayLayer = 0;
-				barrier.subresourceRange.layerCount = 1;
-
-				if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
-					barrier.srcAccessMask = 0;
-					barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-
-					sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
-					destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-				} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
-					barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-					barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
-
-					sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-					destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
-				}
-				else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_GENERAL) {
-					barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-					barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
-
-					sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-					destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
-				}
-				else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_GENERAL) {
-					barrier.srcAccessMask = 0;
-					barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
-
-					sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
-					destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
-				}
-				else {
-					throw love::Exception("unsupported lay transition");
-				}
-
-				vkCmdPipelineBarrier(
-					commandBuffer,
-					sourceStage, destinationStage,
-					0,
-					0, nullptr,
-					0, nullptr,
-					1, &barrier
-				);
+				Vulkan::cmdTransitionImageLayout(commandBuffer, image, oldLayout, newLayout);
 
 
 				vgfx->endSingleTimeCommands(commandBuffer);
 				vgfx->endSingleTimeCommands(commandBuffer);
 			}
 			}

+ 7 - 7
src/modules/graphics/vulkan/Texture.h

@@ -20,18 +20,18 @@ namespace love {
 				virtual bool loadVolatile() override;
 				virtual bool loadVolatile() override;
 				virtual void unloadVolatile() override;
 				virtual void unloadVolatile() override;
 
 
-				void copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override { std::cout << "Texture::copyFromBuffer "; };
-				void copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) override { std::cout << "Texture::copyToBuffer "; };
+				void copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override { };
+				void copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) override { };
 
 
-				ptrdiff_t getRenderTargetHandle() const override { std::cout << "Texture::getRenderTargetHandle "; return (ptrdiff_t)0; };
-				ptrdiff_t getSamplerHandle() const override { std::cout << "Texture::getSamplerHandle "; return (ptrdiff_t)0; };
+				ptrdiff_t getRenderTargetHandle() const override { return (ptrdiff_t)textureImage; };
+				ptrdiff_t getSamplerHandle() const override { return (ptrdiff_t)textureSampler; };
 
 
 				void uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) override;
 				void uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) override;
 
 
-				void generateMipmapsInternal()  override { std::cout << "Texture::generateMipmapsInternal "; };
+				void generateMipmapsInternal()  override { };
 
 
-				int getMSAA() const override { std::cout << "Texture::getMSAA "; return 0; };
-				ptrdiff_t getHandle() const override { std::cout << "Texture::getHandle "; return (ptrdiff_t)textureImage; }
+				int getMSAA() const override { return 0; };
+				ptrdiff_t getHandle() const override { return (ptrdiff_t)textureImage; }
 				VkImageView getImageView() const { return textureImageView; }
 				VkImageView getImageView() const { return textureImageView; }
 				VkSampler getSampler() const { return textureSampler; }
 				VkSampler getSampler() const { return textureSampler; }
 
 

+ 73 - 0
src/modules/graphics/vulkan/Vulkan.cpp

@@ -398,6 +398,79 @@ namespace love {
 					throw love::Exception("unknown cull mode");
 					throw love::Exception("unknown cull mode");
 				}
 				}
 			}
 			}
+
+			void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) {
+				VkImageMemoryBarrier barrier{};
+				barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+				barrier.oldLayout = oldLayout;
+				barrier.newLayout = newLayout;
+				barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+				barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+				barrier.image = image;
+				barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+				barrier.subresourceRange.baseMipLevel = 0;
+				barrier.subresourceRange.levelCount = 1;
+				barrier.subresourceRange.baseArrayLayer = 0;
+				barrier.subresourceRange.layerCount = 1;
+
+				VkPipelineStageFlags sourceStage;
+				VkPipelineStageFlags destinationStage;
+
+				if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
+					barrier.srcAccessMask = 0;
+					barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+					sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+					destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+				}
+				else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+					barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+					barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+
+					sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+					destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+				}
+				else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_GENERAL) {
+					barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+					barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+
+					sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+					destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+				}
+				else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_GENERAL) {
+					barrier.srcAccessMask = 0;
+					barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
+
+					sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
+					destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+				}
+				else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && 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_PRESENT_SRC_KHR) {
+					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!");
+				}
+
+				vkCmdPipelineBarrier(
+					commandBuffer,
+					sourceStage, destinationStage,
+					0,
+					0, nullptr,
+					0, nullptr,
+					1, &barrier
+				);
+			}
 		}
 		}
 	}
 	}
 }
 }

+ 2 - 0
src/modules/graphics/vulkan/Vulkan.h

@@ -29,6 +29,8 @@ namespace love {
 				static VkColorComponentFlags getColorMask(ColorChannelMask);
 				static VkColorComponentFlags getColorMask(ColorChannelMask);
 				static VkFrontFace getFrontFace(Winding);
 				static VkFrontFace getFrontFace(Winding);
 				static VkCullModeFlags getCullMode(CullMode);
 				static VkCullModeFlags getCullMode(CullMode);
+
+				static void cmdTransitionImageLayout(VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout);
 			};
 			};
 		}
 		}
 	}
 	}