From d958c4fec5196b38c493f694a527f2476822e53e Mon Sep 17 00:00:00 2001
From: Magnus Norddahl <dpjudas@users.noreply.github.com>
Date: Tue, 26 Feb 2019 15:29:08 +0100
Subject: [PATCH] - upload and bind some textures

---
 .../vulkan/renderer/vk_renderstate.cpp        |  10 ++
 .../vulkan/system/vk_framebuffer.cpp          |  10 ++
 src/rendering/vulkan/system/vk_framebuffer.h  |   5 +
 .../vulkan/textures/vk_hwtexture.cpp          | 156 +++++++++++++++++-
 src/rendering/vulkan/textures/vk_hwtexture.h  |  35 ++++
 5 files changed, 208 insertions(+), 8 deletions(-)

diff --git a/src/rendering/vulkan/renderer/vk_renderstate.cpp b/src/rendering/vulkan/renderer/vk_renderstate.cpp
index b3a641823..3701fe351 100644
--- a/src/rendering/vulkan/renderer/vk_renderstate.cpp
+++ b/src/rendering/vulkan/renderer/vk_renderstate.cpp
@@ -3,6 +3,7 @@
 #include "vulkan/system/vk_framebuffer.h"
 #include "vulkan/system/vk_builders.h"
 #include "vulkan/renderer/vk_renderpass.h"
+#include "vulkan/textures/vk_hwtexture.h"
 #include "templates.h"
 #include "doomstat.h"
 #include "r_data/colormaps.h"
@@ -165,6 +166,15 @@ void VkRenderState::Apply(int dt)
 	mCommandBuffer->bindIndexBuffer(static_cast<VKIndexBuffer*>(mIndexBuffer)->mBuffer->buffer, 0, VK_INDEX_TYPE_UINT32);
 
 	BindDescriptorSets();
+
+	if (mMaterial.mChanged)
+	{
+		auto base = static_cast<VkHardwareTexture*>(mMaterial.mMaterial->GetLayer(0, mMaterial.mTranslation));
+		if (base)
+			mCommandBuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, passManager->PipelineLayout.get(), 1, base->GetDescriptorSet(mMaterial));
+
+		mMaterial.mChanged = false;
+	}
 }
 
 void VkRenderState::Bind(int bindingpoint, uint32_t offset)
diff --git a/src/rendering/vulkan/system/vk_framebuffer.cpp b/src/rendering/vulkan/system/vk_framebuffer.cpp
index 9b5e5345c..2d30161be 100644
--- a/src/rendering/vulkan/system/vk_framebuffer.cpp
+++ b/src/rendering/vulkan/system/vk_framebuffer.cpp
@@ -43,6 +43,7 @@
 #include "vulkan/renderer/vk_renderpass.h"
 #include "vulkan/shaders/vk_shader.h"
 #include "vulkan/textures/vk_samplers.h"
+#include "vulkan/textures/vk_hwtexture.h"
 #include "vulkan/system/vk_builders.h"
 #include "vulkan/system/vk_swapchain.h"
 #include "doomerrors.h"
@@ -62,6 +63,8 @@ VulkanFrameBuffer::VulkanFrameBuffer(void *hMonitor, bool fullscreen, VulkanDevi
 
 VulkanFrameBuffer::~VulkanFrameBuffer()
 {
+	for (auto tex : AllTextures)
+		tex->Reset();
 }
 
 void VulkanFrameBuffer::InitializeState()
@@ -232,6 +235,13 @@ void VulkanFrameBuffer::CleanForRestart()
 {
 }
 
+IHardwareTexture *VulkanFrameBuffer::CreateHardwareTexture()
+{
+	auto texture = new VkHardwareTexture();
+	AllTextures.Push(texture);
+	return texture;
+}
+
 FModelRenderer *VulkanFrameBuffer::CreateModelRenderer(int mli) 
 {
 	I_FatalError("VulkanFrameBuffer::CreateModelRenderer not implemented\n");
diff --git a/src/rendering/vulkan/system/vk_framebuffer.h b/src/rendering/vulkan/system/vk_framebuffer.h
index 401b8f857..4e2d9d05e 100644
--- a/src/rendering/vulkan/system/vk_framebuffer.h
+++ b/src/rendering/vulkan/system/vk_framebuffer.h
@@ -9,6 +9,7 @@ class VkShaderManager;
 class VkRenderPassManager;
 class VkRenderState;
 class VKDataBuffer;
+class VkHardwareTexture;
 
 class VulkanFrameBuffer : public SystemBaseFrameBuffer
 {
@@ -21,6 +22,7 @@ public:
 	VulkanCommandBuffer *GetUploadCommands();
 	VulkanCommandBuffer *GetDrawCommands();
 	VkShaderManager *GetShaderManager() { return mShaderManager.get(); }
+	VkSamplerManager *GetSamplerManager() { return mSamplerManager.get(); }
 	VkRenderPassManager *GetRenderPassManager() { return mRenderPassManager.get(); }
 	VkRenderState *GetRenderState() { return mRenderState.get(); }
 
@@ -44,6 +46,7 @@ public:
 	void BeginFrame() override;
 	void BlurScene(float amount) override;
 
+	IHardwareTexture *CreateHardwareTexture() override;
 	FModelRenderer *CreateModelRenderer(int mli) override;
 	IShaderProgram *CreateShaderProgram() override;
 	IVertexBuffer *CreateVertexBuffer() override;
@@ -75,6 +78,8 @@ private:
 
 	int lastSwapWidth = 0;
 	int lastSwapHeight = 0;
+
+	TArray<VkHardwareTexture*> AllTextures;
 };
 
 inline VulkanFrameBuffer *GetVulkanFrameBuffer() { return static_cast<VulkanFrameBuffer*>(screen); }
diff --git a/src/rendering/vulkan/textures/vk_hwtexture.cpp b/src/rendering/vulkan/textures/vk_hwtexture.cpp
index db0eb020f..8c498bece 100644
--- a/src/rendering/vulkan/textures/vk_hwtexture.cpp
+++ b/src/rendering/vulkan/textures/vk_hwtexture.cpp
@@ -28,10 +28,155 @@
 #include "c_cvars.h"
 #include "r_data/colormaps.h"
 #include "hwrenderer/textures/hw_material.h"
-
 #include "hwrenderer/utility/hw_cvars.h"
+#include "vulkan/system/vk_objects.h"
+#include "vulkan/system/vk_builders.h"
+#include "vulkan/system/vk_framebuffer.h"
+#include "vulkan/textures/vk_samplers.h"
+#include "vulkan/renderer/vk_renderpass.h"
 #include "vk_hwtexture.h"
 
+VkHardwareTexture::VkHardwareTexture()
+{
+}
+
+VkHardwareTexture::~VkHardwareTexture()
+{
+}
+
+void VkHardwareTexture::Reset()
+{
+	mDescriptorSet.reset();
+	mImage.reset();
+	mImageView.reset();
+	mStagingBuffer.reset();
+}
+
+VulkanDescriptorSet *VkHardwareTexture::GetDescriptorSet(const FMaterialState &state)
+{
+	if (!mImage)
+	{
+		static const uint32_t testpixels[4 * 4] =
+		{
+			0xff0000ff, 0xff0000ff, 0xffff00ff, 0xffff00ff,
+			0xff0000ff, 0xff0000ff, 0xffff00ff, 0xffff00ff,
+			0xff0000ff, 0x00ffffff, 0x0000ffff, 0x0000ffff,
+			0xff0000ff, 0x00ffffff, 0x0000ffff, 0x0000ffff,
+		};
+		CreateTexture(4, 4, 4, VK_FORMAT_R8G8B8A8_UNORM, testpixels);
+	}
+
+	if (!mDescriptorSet)
+	{
+		auto fb = GetVulkanFrameBuffer();
+		mDescriptorSet = fb->GetRenderPassManager()->DescriptorPool->allocate(fb->GetRenderPassManager()->TextureSetLayout.get());
+
+		WriteDescriptors update;
+		update.addCombinedImageSampler(mDescriptorSet.get(), 0, mImageView.get(), fb->GetSamplerManager()->Get(0), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+		update.updateSets(fb->device);
+	}
+
+	return mDescriptorSet.get();
+}
+
+void VkHardwareTexture::CreateTexture(int w, int h, int pixelsize, VkFormat format, const void *pixels)
+{
+	auto fb = GetVulkanFrameBuffer();
+
+	int totalSize = w * h * pixelsize;
+
+	BufferBuilder bufbuilder;
+	bufbuilder.setSize(totalSize);
+	bufbuilder.setUsage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY);
+	mStagingBuffer = bufbuilder.create(fb->device);
+
+	uint8_t *data = (uint8_t*)mStagingBuffer->Map(0, totalSize);
+	memcpy(data, pixels, totalSize);
+	mStagingBuffer->Unmap();
+
+	ImageBuilder imgbuilder;
+	imgbuilder.setFormat(format);
+	imgbuilder.setSize(w, h, GetMipLevels(w, h));
+	imgbuilder.setUsage(VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
+	mImage = imgbuilder.create(fb->device);
+
+	ImageViewBuilder viewbuilder;
+	viewbuilder.setImage(mImage.get(), format);
+	mImageView = viewbuilder.create(fb->device);
+
+	auto cmdbuffer = fb->GetUploadCommands();
+
+	PipelineBarrier imageTransition0;
+	imageTransition0.addImage(mImage.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT);
+	imageTransition0.execute(cmdbuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+	VkBufferImageCopy region = {};
+	region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+	region.imageSubresource.layerCount = 1;
+	region.imageExtent.depth = 1;
+	region.imageExtent.width = w;
+	region.imageExtent.height = h;
+	cmdbuffer->copyBufferToImage(mStagingBuffer->buffer, mImage->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
+
+	GenerateMipmaps(mImage.get(), cmdbuffer);
+}
+
+void VkHardwareTexture::GenerateMipmaps(VulkanImage *image, VulkanCommandBuffer *cmdbuffer)
+{
+	int mipWidth = image->width;
+	int mipHeight = image->height;
+	int i;
+	for (i = 1; mipWidth > 1 || mipHeight > 1; i++)
+	{
+		PipelineBarrier barrier0;
+		barrier0.addImage(image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, i - 1);
+		barrier0.execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+		int nextWidth = std::max(mipWidth >> 1, 1);
+		int nextHeight = std::max(mipHeight >> 1, 1);
+
+		VkImageBlit blit = {};
+		blit.srcOffsets[0] = { 0, 0, 0 };
+		blit.srcOffsets[1] = { mipWidth, mipHeight, 1 };
+		blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+		blit.srcSubresource.mipLevel = i - 1;
+		blit.srcSubresource.baseArrayLayer = 0;
+		blit.srcSubresource.layerCount = 1;
+		blit.dstOffsets[0] = { 0, 0, 0 };
+		blit.dstOffsets[1] = { nextWidth, nextHeight, 1 };
+		blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+		blit.dstSubresource.mipLevel = i;
+		blit.dstSubresource.baseArrayLayer = 0;
+		blit.dstSubresource.layerCount = 1;
+		cmdbuffer->blitImage(image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
+
+		PipelineBarrier barrier1;
+		barrier1.addImage(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, i - 1);
+		barrier1.execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
+
+		mipWidth = nextWidth;
+		mipHeight = nextHeight;
+	}
+
+	PipelineBarrier barrier2;
+	barrier2.addImage(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, i - 1);
+	barrier2.execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
+}
+
+int VkHardwareTexture::GetMipLevels(int w, int h)
+{
+	int levels = 1;
+	while (w > 1 || h > 1)
+	{
+		w = std::max(w >> 1, 1);
+		h = std::max(h >> 1, 1);
+		levels++;
+	}
+	return levels;
+}
+
+#if 0
+
 //===========================================================================
 // 
 //	Creates the low level texture object
@@ -40,7 +185,6 @@
 
 VkResult VkHardwareTexture::CreateTexture(unsigned char * buffer, int w, int h, bool mipmap, int translation)
 {
-#if 0
 	int rh,rw;
 	bool deletebuffer=false;
 	auto tTex = GetTexID(translation);
@@ -72,9 +216,6 @@ VkResult VkHardwareTexture::CreateTexture(unsigned char * buffer, int w, int h,
 		tTex->vkTexture = nullptr;
 	}
 	return res;
-#else
-	return VK_ERROR_INITIALIZATION_FAILED; 
-#endif
 }
 
 //===========================================================================
@@ -204,7 +345,6 @@ VkHardwareTexture::TranslatedTexture *VkHardwareTexture::GetTexID(int translatio
 
 VkTexture *VkHardwareTexture::GetVkTexture(FTexture *tex, int translation, bool needmipmap, int flags)
 {
-#if 0
 	int usebright = false;
 
 	if (translation <= 0)
@@ -227,6 +367,6 @@ VkTexture *VkHardwareTexture::GetVkTexture(FTexture *tex, int translation, bool
 		delete[] buffer;
 	}
 	return pTex->vkTexture;
-#endif
-	return nullptr;
 }
+
+#endif
diff --git a/src/rendering/vulkan/textures/vk_hwtexture.h b/src/rendering/vulkan/textures/vk_hwtexture.h
index d30b89132..3a7b8a977 100644
--- a/src/rendering/vulkan/textures/vk_hwtexture.h
+++ b/src/rendering/vulkan/textures/vk_hwtexture.h
@@ -11,6 +11,40 @@
 #include "hwrenderer/textures/hw_ihwtexture.h"
 #include "volk/volk.h"
 
+struct FMaterialState;
+class VulkanDescriptorSet;
+class VulkanImage;
+class VulkanImageView;
+class VulkanBuffer;
+
+class VkHardwareTexture : public IHardwareTexture
+{
+public:
+	VkHardwareTexture();
+	~VkHardwareTexture();
+
+	void Reset();
+
+	VulkanDescriptorSet *GetDescriptorSet(const FMaterialState &state);
+
+	// Software renderer stuff
+	void AllocateBuffer(int w, int h, int texelsize) override { }
+	uint8_t *MapBuffer() override { return nullptr; }
+	unsigned int CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, int translation, const char *name) override { return 0; }
+
+private:
+	void CreateTexture(int w, int h, int pixelsize, VkFormat format, const void *pixels);
+	void GenerateMipmaps(VulkanImage *image, VulkanCommandBuffer *cmdbuffer);
+	static int GetMipLevels(int w, int h);
+
+	std::unique_ptr<VulkanDescriptorSet> mDescriptorSet;
+	std::unique_ptr<VulkanImage> mImage;
+	std::unique_ptr<VulkanImageView> mImageView;
+	std::unique_ptr<VulkanBuffer> mStagingBuffer;
+};
+
+#if 0
+
 class FCanvasTexture;
 class AActor;
 class VkTexture;
@@ -71,3 +105,4 @@ public:
 	VkTexture *GetVkTexture(FTexture *tex, int translation, bool needmipmap, int flags);
 };
 
+#endif