void Image::cmdPipelineBarrier(const VkCommandBuffer cmdBuffer, const VkAccessFlags dstAccessMask, const VkImageLayout newLayout, const VkImageSubresourceRange& subresourceRange) { if (newLayout == VK_IMAGE_LAYOUT_UNDEFINED || newLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) { logPrint(VKTS_LOG_WARNING, "Image: New layout not allowed: %d", newLayout); return; } VkImageMemoryBarrier imageMemoryBarrier; memset(&imageMemoryBarrier, 0, sizeof(VkImageMemoryBarrier)); imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageMemoryBarrier.dstAccessMask = dstAccessMask; imageMemoryBarrier.newLayout = newLayout; imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; imageMemoryBarrier.image = image; imageMemoryBarrier.subresourceRange = subresourceRange; // It is allowed, that each mip level can have different layouts. for (uint32_t arrayLayer = subresourceRange.baseArrayLayer; arrayLayer < subresourceRange.baseArrayLayer + subresourceRange.layerCount; arrayLayer++) { for (uint32_t mipLevel = subresourceRange.baseMipLevel; mipLevel < subresourceRange.baseMipLevel + subresourceRange.levelCount; mipLevel++) { if (accessMask[arrayLayer * getMipLevels() + mipLevel] == dstAccessMask && imageLayout[arrayLayer * getMipLevels() + mipLevel] == newLayout) { continue; } imageMemoryBarrier.srcAccessMask = accessMask[arrayLayer * getMipLevels() + mipLevel]; imageMemoryBarrier.oldLayout = imageLayout[arrayLayer * getMipLevels() + mipLevel]; imageMemoryBarrier.subresourceRange.baseMipLevel = mipLevel; imageMemoryBarrier.subresourceRange.levelCount = 1; imageMemoryBarrier.subresourceRange.baseArrayLayer = arrayLayer; imageMemoryBarrier.subresourceRange.layerCount = 1; vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); accessMask[arrayLayer * getMipLevels() + mipLevel] = dstAccessMask; imageLayout[arrayLayer * getMipLevels() + mipLevel] = newLayout; } } }
ErrorCode DXTexture2D::setData( int xOffset, int yOffset, int width, int height, void* data, TextureFormat::Format format ) { // update if already valid if( _shaderResourceView != nullptr ) { // format must be the same if( format != _format ) { return ErrorCode::CIRI_INVALID_ARGUMENT; } // for now, the enture texture must be updated, and it must therefore be of the same size if( width != _width || height != _height || xOffset != 0 || yOffset != 0 ) { return ErrorCode::CIRI_NOT_IMPLEMENTED; } // todo: support editing (also change below to dynamic) return ErrorCode::CIRI_NOT_IMPLEMENTED; } // offsets must be zero when initializing the texture if( xOffset != 0 || yOffset != 0 ) { return ErrorCode::CIRI_INVALID_ARGUMENT; } // width and height must be positive if( width <= 0 || height <= 0 ) { return ErrorCode::CIRI_INVALID_ARGUMENT; } _width = width; _height = height; _format = format; D3D11_TEXTURE2D_DESC texDesc; ZeroMemory(&texDesc, sizeof(texDesc)); texDesc.Width = width; texDesc.Height = height; texDesc.MipLevels = getMipLevels(); texDesc.ArraySize = 1; // todo: arraySize (for cube textures and whatnot) texDesc.Format = ciriToDxTextureFormat(format); texDesc.SampleDesc.Count = 1; texDesc.SampleDesc.Quality = 0; texDesc.Usage = getUsage(); texDesc.BindFlags = getBindFlags(); texDesc.CPUAccessFlags = getCpuFlags(); texDesc.MiscFlags = getMiscFlags(); // multisampled textures require initialization with nullptr first, so just do it this way for everything for simplicity if( FAILED(_device->getDevice()->CreateTexture2D(&texDesc, nullptr, &_texture2D)) ) { destroy(); return ErrorCode::CIRI_UNKNOWN_ERROR; // todo: texture create failure } // update the subresource (a.k.a set pixel data) if there is any if( data != nullptr ) { const UINT pitch = width * TextureFormat::bytesPerPixel(format); _device->getContext()->UpdateSubresource(_texture2D, 0, nullptr, data, pitch, 0); } // create actual shader resource view if( FAILED(_device->getDevice()->CreateShaderResourceView(_texture2D, nullptr, &_shaderResourceView)) ) { destroy(); return ErrorCode::CIRI_UNKNOWN_ERROR; // todo: texture create failure } // generate mipmaps if( _flags & TextureFlags::Mipmaps ) { _device->getContext()->GenerateMips(_shaderResourceView); } return ErrorCode::CIRI_OK; }
VkAccessFlags Image::getAccessMask(const uint32_t mipLevel, const uint32_t arrayLayer) const { return accessMask[arrayLayer * getMipLevels() + mipLevel]; }
VkImageLayout Image::getImageLayout(const uint32_t mipLevel, const uint32_t arrayLayer) const { return imageLayout[arrayLayer * getMipLevels() + mipLevel]; }
vkts::IImageDataSP Example::gatherImageData() const { VkResult result; auto fence = vkts::fenceCreate(device->getDevice(), 0); if (!fence.get()) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not create fence."); return vkts::IImageDataSP(); } // auto imageData = vkts::imageDataCreate(VKTS_IMAGE_NAME, VKTS_IMAGE_LENGTH, VKTS_IMAGE_LENGTH, 1, 1.0f, 0.0f, 0.0f, 1.0f, VK_IMAGE_TYPE_2D, VK_FORMAT_R8G8B8A8_UNORM); // Check, if we can use a linear tiled image for staging. if (physicalDevice->isImageTilingAvailable(VK_IMAGE_TILING_LINEAR, imageData->getFormat(), imageData->getImageType(), 0, imageData->getExtent3D(), imageData->getMipLevels(), 1, VK_SAMPLE_COUNT_1_BIT, imageData->getSize())) { vkts::IImageSP stageImage; vkts::IDeviceMemorySP stageDeviceMemory; if (!createTexture(stageImage, stageDeviceMemory, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 0)) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not create stage image and device memory."); return vkts::IImageDataSP(); } // cmdBuffer->reset(); result = cmdBuffer->beginCommandBuffer(0, VK_NULL_HANDLE, 0, VK_NULL_HANDLE, VK_FALSE, 0, 0); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not begin command buffer."); return vkts::IImageDataSP(); } VkImageSubresourceRange imageSubresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; // Prepare stage image for final layout etc. stageImage->cmdPipelineBarrier(cmdBuffer->getCommandBuffer(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageSubresourceRange); VkImageCopy imageCopy; imageCopy.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; imageCopy.srcOffset = {0, 0, 0}; imageCopy.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; imageCopy.dstOffset = {0, 0, 0}; imageCopy.extent = { VKTS_IMAGE_LENGTH, VKTS_IMAGE_LENGTH, 1u }; // Copy form device to host visible image / memory. This command also sets the needed barriers. image->copyImage(cmdBuffer->getCommandBuffer(), stageImage, imageCopy); stageImage->cmdPipelineBarrier(cmdBuffer->getCommandBuffer(), VK_ACCESS_HOST_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, imageSubresourceRange); result = cmdBuffer->endCommandBuffer(); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not end command buffer."); return VK_FALSE; } VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = cmdBuffer->getCommandBuffers(); submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; result = queue->submit(1, &submitInfo, fence->getFence()); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not submit queue."); return vkts::IImageDataSP(); } // result = fence->waitForFence(UINT64_MAX); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not wait for fence."); return vkts::IImageDataSP(); } // // Copy pixel data from device memory into image data memory. // VkImageSubresource imageSubresource; imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageSubresource.mipLevel = 0; imageSubresource.arrayLayer = 0; VkSubresourceLayout subresourceLayout; stageImage->getImageSubresourceLayout(subresourceLayout, imageSubresource); // result = stageDeviceMemory->mapMemory(0, VK_WHOLE_SIZE, 0); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not map memory."); return vkts::IImageDataSP(); } imageData->upload(stageDeviceMemory->getMemory(), 0, 0, subresourceLayout); if (!(stageDeviceMemory->getMemoryPropertyFlags() & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { result = stageDeviceMemory->invalidateMappedMemoryRanges(0, VK_WHOLE_SIZE); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not invalidate memory."); return VK_FALSE; } } stageDeviceMemory->unmapMemory(); // Stage image and device memory are automatically destroyed. } else { // As an alternative, use the buffer. vkts::IBufferSP stageBuffer; vkts::IDeviceMemorySP stageDeviceMemory; VkBufferCreateInfo bufferCreateInfo{}; bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCreateInfo.size = imageData->getSize(); bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; bufferCreateInfo.flags = 0; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; bufferCreateInfo.queueFamilyIndexCount = 0; bufferCreateInfo.pQueueFamilyIndices = nullptr; if (!createBuffer(stageBuffer, stageDeviceMemory, bufferCreateInfo, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not create buffer."); return vkts::IImageDataSP(); } // cmdBuffer->reset(); result = cmdBuffer->beginCommandBuffer(0, VK_NULL_HANDLE, 0, VK_NULL_HANDLE, VK_FALSE, 0, 0); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not begin command buffer."); return vkts::IImageDataSP(); } VkBufferImageCopy bufferImageCopy; bufferImageCopy.bufferOffset = 0; bufferImageCopy.bufferRowLength = VKTS_IMAGE_LENGTH; bufferImageCopy.bufferImageHeight = VKTS_IMAGE_LENGTH; bufferImageCopy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; bufferImageCopy.imageOffset = {0, 0, 0}; bufferImageCopy.imageExtent = {VKTS_IMAGE_LENGTH, VKTS_IMAGE_LENGTH, 1}; image->copyImageToBuffer(cmdBuffer->getCommandBuffer(), stageBuffer, bufferImageCopy); result = cmdBuffer->endCommandBuffer(); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not end command buffer."); return VK_FALSE; } VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = cmdBuffer->getCommandBuffers(); submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; result = queue->submit(1, &submitInfo, fence->getFence()); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not submit queue."); return vkts::IImageDataSP(); } // result = fence->waitForFence(UINT64_MAX); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not wait for fence."); return vkts::IImageDataSP(); } // // Copy pixel data from device memory into image data memory. // VkSubresourceLayout subresourceLayout; subresourceLayout.offset = 0; subresourceLayout.size = stageBuffer->getSize(); subresourceLayout.rowPitch = VKTS_IMAGE_LENGTH * 4 * sizeof(uint8_t); subresourceLayout.arrayPitch = VKTS_IMAGE_LENGTH * VKTS_IMAGE_LENGTH * 4 * sizeof(uint8_t); subresourceLayout.depthPitch = VKTS_IMAGE_LENGTH * VKTS_IMAGE_LENGTH * 4 * sizeof(uint8_t); result = stageDeviceMemory->mapMemory(0, VK_WHOLE_SIZE, 0); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not map memory."); return vkts::IImageDataSP(); } imageData->upload(stageDeviceMemory->getMemory(), 0, 0, subresourceLayout); if (!(stageDeviceMemory->getMemoryPropertyFlags() & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { result = stageDeviceMemory->invalidateMappedMemoryRanges(0, VK_WHOLE_SIZE); if (result != VK_SUCCESS) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not invalidate memory."); return VK_FALSE; } } stageDeviceMemory->unmapMemory(); // Stage image and device memory are automatically destroyed. } // Fence is automatically destroyed. return imageData; }