Cogs.Core
TexturesVK.cpp
1#include "TexturesVK.h"
2
3#include "GraphicsDeviceVK.h"
4
5#include "FormatsVK.h"
6
7namespace
8{
9 Cogs::Logging::Log logger = Cogs::Logging::getLogger("TexturesVK");
10}
11
12void Cogs::TexturesVK::initialize(GraphicsDeviceVK * grapihcsDevice)
13{
14 this->graphicsDevice = grapihcsDevice;
15 device = grapihcsDevice->device;
16}
17
19{
20 auto & texture = textures[textureHandle];
21
22 vkDestroyImageView(device, texture.imageView, nullptr);
23
24 if (!texture.external) {
25 vkDestroyImage(device, texture.image, nullptr);
26 }
27}
28
30{
31 SamplerVK sampler = {};
32
33 VkFilter filters[] = {
34 VK_FILTER_NEAREST,
35 VK_FILTER_LINEAR,
36 VK_FILTER_NEAREST,
37 VK_FILTER_LINEAR,
38 };
39
40 VkSamplerMipmapMode mipModes[] = {
41 VK_SAMPLER_MIPMAP_MODE_NEAREST,
42 VK_SAMPLER_MIPMAP_MODE_LINEAR,
43 VK_SAMPLER_MIPMAP_MODE_NEAREST,
44 VK_SAMPLER_MIPMAP_MODE_LINEAR,
45 };
46
47 VkSamplerAddressMode addressModes[] = {
48 VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
49 VK_SAMPLER_ADDRESS_MODE_REPEAT,
50 VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
51 VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
52 };
53
54 VkCompareOp comp[] = {
55 VK_COMPARE_OP_NEVER,
56 VK_COMPARE_OP_LESS,
57 VK_COMPARE_OP_EQUAL,
58 VK_COMPARE_OP_LESS_OR_EQUAL,
59 VK_COMPARE_OP_GREATER,
60 VK_COMPARE_OP_NOT_EQUAL,
61 VK_COMPARE_OP_GREATER_OR_EQUAL,
62 VK_COMPARE_OP_ALWAYS,
63 };
64
65 VkSamplerCreateInfo samplerInfo = {};
66 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
67 samplerInfo.pNext = nullptr;
68
69 samplerInfo.magFilter = filters[state.filter];
70 samplerInfo.minFilter = filters[state.filter];
71 samplerInfo.mipmapMode = mipModes[state.filter];
72
73 samplerInfo.addressModeU = addressModes[state.addressModeS];
74 samplerInfo.addressModeV = addressModes[state.addressModeT];
75 samplerInfo.addressModeW = addressModes[state.addressModeW];
76
77 samplerInfo.compareEnable = state.filter > 1;
78 samplerInfo.compareOp = comp[state.comparisonFunction];
79
80 samplerInfo.mipLodBias = 0.0f;
81 samplerInfo.minLod = 0.0f;
82 samplerInfo.maxLod = 16.0f;
83
84 samplerInfo.maxAnisotropy = static_cast<float>(state.maxAnisotropy);
85 samplerInfo.anisotropyEnable = state.maxAnisotropy != 0;
86
87 samplerInfo.borderColor = state.borderColor[3] == 1 ?
88 (state.borderColor[0] == 1 ? VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE : VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK) :
89 VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
90
91 auto result = vkCreateSampler(device, &samplerInfo, nullptr, &sampler.sampler);
92
93 if (VK_FAILED(result)) {
94 VK_LOG_ERROR(result, "Could not load sampler.");
96 }
97
98 return samplers.addResource(sampler);
99}
100
102{
103}
104
106{
107}
108
110{
111}
112
114{
115 uint32_t width = desc.width;
116 uint32_t height = desc.height;
117
118 TextureVK texture = {};
119 texture.width = width;
120 texture.height = height;
121 texture.samples = desc.samples;
122 texture.layout = VK_IMAGE_LAYOUT_UNDEFINED;
123
124 const auto vkFormat = Vulkan::TextureFormats[(int)desc.format];
125 texture.format = desc.format;
126 texture.vkFormat = vkFormat;
127
128 bool isDepth = (desc.flags & TextureFlags::DepthBuffer) != 0;
129 const bool isStaging = (desc.flags & TextureFlagsVK::Staging) != 0;
130 const bool isRenderTarget = (desc.flags & TextureFlags::RenderTarget) != 0;
131
132 VkImageUsageFlags vkUsage = 0;
133 VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
134 VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
135
136 if (isRenderTarget) {
137 vkUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
138 }
139
140 if (isDepth) {
141 vkUsage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
142 isDepth = true;
143 texture.isDepth = true;
144
145 if (desc.flags & TextureFlags::Texture) {
146 vkUsage |= VK_IMAGE_USAGE_SAMPLED_BIT;
147 }
148 } else if (isStaging) {
149 vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
150 tiling = VK_IMAGE_TILING_LINEAR;
151 initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
152 } else {
153 vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
154 }
155
156 if ((desc.flags & TextureFlags::ExternalTexture) == 0) {
157 VkImageCreateInfo imageCreateInfo = {};
158 imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
159 imageCreateInfo.pNext = nullptr;
160 imageCreateInfo.initialLayout = initialLayout;
161 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
162 imageCreateInfo.format = vkFormat;
163 imageCreateInfo.extent = { width, height, 1 };
164 imageCreateInfo.mipLevels = desc.levels;
165 imageCreateInfo.arrayLayers = desc.layers;
166 imageCreateInfo.samples = (VkSampleCountFlagBits)desc.samples;
167 imageCreateInfo.tiling = tiling;
168 imageCreateInfo.usage = vkUsage;
169 imageCreateInfo.flags = 0;
170
171 auto result = vkCreateImage(device, &imageCreateInfo, nullptr, &texture.image);
172
173 if (VK_FAILED(result)) {
174 VK_LOG_ERROR(result, "Could not create image.");
176 }
177
178 VkMemoryAllocateInfo allocateInfo = {};
179 allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
180 allocateInfo.pNext = nullptr;
181 allocateInfo.allocationSize = 0;
182 allocateInfo.memoryTypeIndex = 0;
183
184 VkMemoryRequirements memoryRequirements;
185 vkGetImageMemoryRequirements(device, texture.image, &memoryRequirements);
186
187 VkFlags memoryFlags = isStaging ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT : 0;
188 uint32_t typeIndex = 0;
189 graphicsDevice->getMemoryType(memoryRequirements.memoryTypeBits, memoryFlags, &typeIndex);
190
191 allocateInfo.allocationSize = memoryRequirements.size;
192 allocateInfo.memoryTypeIndex = typeIndex;
193
194 result = vkAllocateMemory(device, &allocateInfo, nullptr, &texture.deviceMemory);
195
196 if (VK_FAILED(result)) {
197 VK_LOG_ERROR(result, "Could not allocate device memory.");
199 }
200
201 result = vkBindImageMemory(device, texture.image, texture.deviceMemory, 0);
202
203 if (VK_FAILED(result)) {
204 VK_LOG_ERROR(result, "Could not bind image memory.");
206 }
207
208 if (data) {
209 updateTextureData(data, desc.layers, desc.levels, texture);
210 }
211 } else {
212 texture.image = (VkImage)data->externalHandle;
213 texture.external = true;
214 }
215
216 if (!isStaging) {
217 VkImageViewCreateInfo imageViewInfo = {};
218 imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
219 imageViewInfo.image = texture.image;
220 imageViewInfo.format = vkFormat;
221
222 // Only map depth component to R channel to emulate D3D behavior.
223 VkComponentMapping swizzle = isDepth ?
224 VkComponentMapping{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY } :
225 VkComponentMapping{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
226
227 imageViewInfo.components = swizzle;
228 imageViewInfo.subresourceRange.aspectMask = isDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
229 imageViewInfo.subresourceRange.baseMipLevel = 0;
230 imageViewInfo.subresourceRange.levelCount = desc.levels;
231 imageViewInfo.subresourceRange.baseArrayLayer = 0;
232 imageViewInfo.subresourceRange.layerCount = desc.layers;
233 imageViewInfo.flags = 0;
234 imageViewInfo.viewType = desc.layers > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
235
236 auto result = vkCreateImageView(device, &imageViewInfo, nullptr, &texture.imageView);
237
238 if (VK_FAILED(result)) {
239 VK_LOG_ERROR(result, "Could not create image view.");
241 }
242 }
243
244 return textures.addResource(texture);
245}
246
247void Cogs::TexturesVK::updateTextureData(const TextureData * data, const uint32_t arraySize, const uint32_t numLevels, TextureVK & texture)
248{
249 VkImageSubresourceRange range;
250 range.aspectMask = texture.isDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
251 range.baseArrayLayer = 0;
252 range.baseMipLevel = 0;
253 range.layerCount = arraySize;
254 range.levelCount = numLevels;
255
256 setImageLayout(graphicsDevice->getCommandBuffer(),
257 texture,
258 range.aspectMask,
259 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
260 range);
261
262 for (uint32_t a = 0; a < arraySize; ++a) {
263 for (uint32_t m = 0; m < numLevels; ++m) {
264 const uint32_t index = a * numLevels + m;
265
266 const uint32_t width = texture.width / static_cast<uint32_t>(std::pow(2ul, m));
267 const uint32_t height = texture.height / static_cast<uint32_t>(std::pow(2ul, m));
268
269 auto uploadHandle = loadTexture(nullptr, width, height, texture.format, TextureFlagsVK::Staging);
270
271 if (!HandleIsValid(uploadHandle)) {
272 LOG_ERROR(logger, "Could not create staging texture.");
273 return;
274 }
275
276 TextureVK uploadTexture = textures[uploadHandle];
277
278 VkImageSubresource imageSubresource = {};
279 imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
280 imageSubresource.arrayLayer = a;
281 imageSubresource.mipLevel = m;
282
283 VkSubresourceLayout resourceLayout;
284 vkGetImageSubresourceLayout(device, uploadTexture.image, &imageSubresource, &resourceLayout);
285
286 const size_t bpp = Cogs::getBlockSize(texture.format);
287
288 void * vData;
289 auto result = vkMapMemory(device, uploadTexture.deviceMemory, 0, VK_WHOLE_SIZE, 0, &vData);
290
291 if (VK_FAILED(result)) {
292 VK_LOG_ERROR(result, "Could not map upload memory.");
293 }
294
295 auto mappedData = static_cast<uint8_t *>(vData);
296 const auto sourceData = static_cast<const uint8_t *>(data->getData(a, 0, m));
297 const auto sourcePitch = data->getPitch(m);
298 const auto rows = resourceLayout.size / resourceLayout.rowPitch;
299
300 for (size_t i = 0; i < rows; ++i) {
301 std::memcpy(mappedData, sourceData + i * sourcePitch, sourcePitch);
302 mappedData += resourceLayout.rowPitch;
303 }
304
305 vkUnmapMemory(device, uploadTexture.deviceMemory);
306
307 setImageLayout(graphicsDevice->getCommandBuffer(),
308 uploadTexture,
309 VK_IMAGE_ASPECT_COLOR_BIT,
310 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
311
312 VkImageCopy copyRegion = {};
313
314 copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
315 copyRegion.srcSubresource.baseArrayLayer = 0;
316 copyRegion.srcSubresource.mipLevel = 0;
317 copyRegion.srcSubresource.layerCount = 1;
318 copyRegion.srcOffset = { 0, 0, 0 };
319
320 copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
321 copyRegion.dstSubresource.baseArrayLayer = a;
322 copyRegion.dstSubresource.mipLevel = m;
323 copyRegion.dstSubresource.layerCount = 1;
324 copyRegion.dstOffset = { 0, 0, 0 };
325
326 copyRegion.extent.width = width;
327 copyRegion.extent.height = height;
328 copyRegion.extent.depth = 1;
329
330 vkCmdCopyImage(
331 graphicsDevice->getCommandBuffer(),
332 uploadTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
333 texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
334 1, &copyRegion);
335 }
336 }
337
338 setImageLayout(graphicsDevice->getCommandBuffer(),
339 texture,
340 range.aspectMask,
341 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
342 range);
343}
344
346{
347 return TextureViewHandle();
348}
349
351{
352}
353
355{
356 return nullptr;
357}
Log implementation class.
Definition: LogManager.h:139
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
static const Handle_t InvalidHandle
Represents an invalid handle.
Definition: Common.h:80
Encapsulates state for texture sampling in a state object.
Definition: SamplerState.h:12
ComparisonFunction comparisonFunction
Specifies the comparison function to use when applying a comparison sampler.
Definition: SamplerState.h:73
unsigned int maxAnisotropy
Specifies the maximum number of anisotropic samples to use when sampling a texture.
Definition: SamplerState.h:76
AddressMode addressModeW
Specifies the addressing mode along the W axis in texture coordinate space.
Definition: SamplerState.h:67
AddressMode addressModeS
Specifies the addressing mode along the S axis in texture coordinate space.
Definition: SamplerState.h:63
float borderColor[4]
Definition: SamplerState.h:80
FilterMode filter
Specifies the filter to use for texture sampling.
Definition: SamplerState.h:70
AddressMode addressModeT
Specifies the addressing mode along the T axis in texture coordinate space.
Definition: SamplerState.h:65
@ DepthBuffer
The texture can be used as a depth target and have depth buffer values written into.
Definition: Flags.h:122
@ RenderTarget
The texture can be used as a render target and drawn into.
Definition: Flags.h:120
@ Texture
Texture usage, see Default.
Definition: Flags.h:118
Describes how to fetch data from a texture in shaders.
Definition: ITextures.h:13
TextureHandle loadTexture(const TextureDescription &desc, const TextureData *data) override
Load a texture from the given description.
Definition: TexturesVK.cpp:113
void releaseSamplerState(SamplerStateHandle handle) override
Release the sampler state with the given handle.
Definition: TexturesVK.cpp:101
void releaseTextureView(const TextureViewHandle &handle) override
Release the given texture view.
Definition: TexturesVK.cpp:350
SamplerStateHandle loadSamplerState(const SamplerState &state) override
Load a sampler state object.
Definition: TexturesVK.cpp:29
void releaseTexture(TextureHandle textureHandle) override
Release the texture with the given textureHandle.
Definition: TexturesVK.cpp:18
void releaseResources() override
Release all allocated texture resources.
Definition: TexturesVK.cpp:109
TextureViewHandle createTextureView(TextureViewDescription &viewDescription) override
Create a texture view used to bind a limited view of the texture data to the rendering pipeline.
Definition: TexturesVK.cpp:345
void generateMipmaps(TextureHandle textureHandle) override
Use the graphics device to generate mipmaps for the texture with the given texture handle.
Definition: TexturesVK.cpp:105
void * getNativeHandle(TextureHandle textureHandle) override
Get the device-specific handle (D3D texture pointer, OpenGL texture ID etc) associated with the given...
Definition: TexturesVK.cpp:354