mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
[MG_Backend] (Disaggregated): generate packed float mips with native shader blits
This commit is contained in:
@@ -478,6 +478,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Bool BlitWireColorToDefault(WireImage source, WireImage destination,
|
Bool BlitWireColorToDefault(WireImage source, WireImage destination,
|
||||||
GLint sx0, GLint sy0, GLint sx1, GLint sy1,
|
GLint sx0, GLint sy0, GLint sx1, GLint sy1,
|
||||||
GLint dx0, GLint dy0, GLint dx1, GLint dy1, GLenum filter);
|
GLint dx0, GLint dy0, GLint dx1, GLint dy1, GLenum filter);
|
||||||
|
Bool BlitWireColorImage(WireImage source, WireImage destination,
|
||||||
|
GLint sx0, GLint sy0, GLint sx1, GLint sy1,
|
||||||
|
GLint dx0, GLint dy0, GLint dx1, GLint dy1, GLenum filter, Bool mipmap);
|
||||||
struct WireColorBlitResources;
|
struct WireColorBlitResources;
|
||||||
WireColorBlitResources* m_wireColorBlitResources = nullptr;
|
WireColorBlitResources* m_wireColorBlitResources = nullptr;
|
||||||
void DestroyWireColorBlitResources();
|
void DestroyWireColorBlitResources();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
struct VulkanRenderer::WireColorBlitResources {
|
struct VulkanRenderer::WireColorBlitResources {
|
||||||
struct Target {
|
struct Target {
|
||||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||||
|
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
VkRenderPass renderPass = VK_NULL_HANDLE;
|
VkRenderPass renderPass = VK_NULL_HANDLE;
|
||||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||||
};
|
};
|
||||||
@@ -38,15 +39,25 @@
|
|||||||
Bool VulkanRenderer::BlitWireColorToDefault(WireImage source, WireImage destination,
|
Bool VulkanRenderer::BlitWireColorToDefault(WireImage source, WireImage destination,
|
||||||
GLint sx0, GLint sy0, GLint sx1, GLint sy1,
|
GLint sx0, GLint sy0, GLint sx1, GLint sy1,
|
||||||
GLint dx0, GLint dy0, GLint dx1, GLint dy1, GLenum filter) {
|
GLint dx0, GLint dy0, GLint dx1, GLint dy1, GLenum filter) {
|
||||||
if (!destination.isDefault || source.isDefault || source.image == destination.image ||
|
if (!destination.isDefault) return false;
|
||||||
|
return BlitWireColorImage(source, destination, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, filter, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool VulkanRenderer::BlitWireColorImage(WireImage source, WireImage destination,
|
||||||
|
GLint sx0, GLint sy0, GLint sx1, GLint sy1,
|
||||||
|
GLint dx0, GLint dy0, GLint dx1, GLint dy1, GLenum filter, Bool mipmap) {
|
||||||
|
const Bool sameImage = source.image == destination.image;
|
||||||
|
if (source.isDefault || (sameImage && source.level == destination.level && source.layer == destination.layer) ||
|
||||||
source.samples != VK_SAMPLE_COUNT_1_BIT || destination.samples != VK_SAMPLE_COUNT_1_BIT ||
|
source.samples != VK_SAMPLE_COUNT_1_BIT || destination.samples != VK_SAMPLE_COUNT_1_BIT ||
|
||||||
source.is3D || (source.viewType != VK_IMAGE_VIEW_TYPE_2D &&
|
source.is3D || destination.is3D || (source.viewType != VK_IMAGE_VIEW_TYPE_2D &&
|
||||||
source.viewType != VK_IMAGE_VIEW_TYPE_2D_ARRAY) ||
|
source.viewType != VK_IMAGE_VIEW_TYPE_2D_ARRAY) ||
|
||||||
|
(destination.viewType != VK_IMAGE_VIEW_TYPE_2D && destination.viewType != VK_IMAGE_VIEW_TYPE_2D_ARRAY) ||
|
||||||
!vkuFormatIsSampledFloat(source.format)) return false;
|
!vkuFormatIsSampledFloat(source.format)) return false;
|
||||||
if (sx0 == sx1 || sy0 == sy1 || dx0 == dx1 || dy0 == dy1) return true;
|
if (sx0 == sx1 || sy0 == sy1 || dx0 == dx1 || dy0 == dy1) return true;
|
||||||
if (!source.extent.width || !source.extent.height ||
|
if (!source.extent.width || !source.extent.height ||
|
||||||
!destination.extent.width || !destination.extent.height) return false;
|
!destination.extent.width || !destination.extent.height) return false;
|
||||||
const auto transform = m_swapchainObject.GetPreTransform();
|
const auto transform = destination.isDefault ? m_swapchainObject.GetPreTransform() :
|
||||||
|
VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||||
if (transform != VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR &&
|
if (transform != VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR &&
|
||||||
transform != VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR &&
|
transform != VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR &&
|
||||||
transform != VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR &&
|
transform != VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR &&
|
||||||
@@ -56,6 +67,15 @@
|
|||||||
const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
|
const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
|
||||||
(filter == GL_LINEAR ? VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT : 0);
|
(filter == GL_LINEAR ? VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT : 0);
|
||||||
if ((sourceProperties.optimalTilingFeatures & required) != required) return false;
|
if ((sourceProperties.optimalTilingFeatures & required) != required) return false;
|
||||||
|
// Mip generation samples and writes the texture's own color space;
|
||||||
|
// GL_FRAMEBUFFER_SRGB only controls framebuffer operations.
|
||||||
|
if (!mipmap) destination.format = ResolveSrgbAttachmentWriteFormat(destination.format,
|
||||||
|
MGB_CTX->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb));
|
||||||
|
VkFormatProperties destinationProperties{};
|
||||||
|
vkGetPhysicalDeviceFormatProperties(m_physicalDevice.handle, destination.format, &destinationProperties);
|
||||||
|
if (!(destinationProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) return false;
|
||||||
|
const VkImageLayout sourceLayout = sameImage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
const VkImageLayout destinationLayout = sameImage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
|
|
||||||
struct Constants {
|
struct Constants {
|
||||||
float srcRect[4];
|
float srcRect[4];
|
||||||
@@ -97,17 +117,15 @@
|
|||||||
VK_VERIFY(vkCreateSampler(m_device, &sampler, nullptr, &cache.linear));
|
VK_VERIFY(vkCreateSampler(m_device, &sampler, nullptr, &cache.linear));
|
||||||
}
|
}
|
||||||
auto& cache = *m_wireColorBlitResources;
|
auto& cache = *m_wireColorBlitResources;
|
||||||
// Honor the same sRGB write policy as wire draw attachments.
|
|
||||||
destination.format = ResolveSrgbAttachmentWriteFormat(destination.format,
|
|
||||||
MGB_CTX->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb));
|
|
||||||
WireColorBlitResources::Target* target = nullptr;
|
WireColorBlitResources::Target* target = nullptr;
|
||||||
for (auto& entry : cache.targets) {
|
for (auto& entry : cache.targets) {
|
||||||
if (entry.format == destination.format) { target = &entry; break; }
|
if (entry.format == destination.format && entry.layout == destinationLayout) { target = &entry; break; }
|
||||||
}
|
}
|
||||||
if (!target) {
|
if (!target) {
|
||||||
cache.targets.emplace_back();
|
cache.targets.emplace_back();
|
||||||
target = &cache.targets.back();
|
target = &cache.targets.back();
|
||||||
target->format = destination.format;
|
target->format = destination.format;
|
||||||
|
target->layout = destinationLayout;
|
||||||
VkAttachmentDescription attachment{};
|
VkAttachmentDescription attachment{};
|
||||||
attachment.format = destination.format;
|
attachment.format = destination.format;
|
||||||
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
@@ -115,8 +133,8 @@
|
|||||||
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||||
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||||
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||||
attachment.initialLayout = attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
attachment.initialLayout = attachment.finalLayout = destinationLayout;
|
||||||
const VkAttachmentReference color{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
const VkAttachmentReference color{0, destinationLayout};
|
||||||
VkSubpassDescription subpass{};
|
VkSubpassDescription subpass{};
|
||||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||||
subpass.colorAttachmentCount = 1;
|
subpass.colorAttachmentCount = 1;
|
||||||
@@ -174,8 +192,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_textureManager->FlushPendingUploads();
|
m_textureManager->FlushPendingUploads();
|
||||||
TransitionWireImage(source, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
// Distinct mip/layer views of the same image are legal shader input and
|
||||||
TransitionWireImage(destination, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
// output. GENERAL keeps the whole-image layout tracker truthful; the
|
||||||
|
// transition also orders the previous mip's attachment write.
|
||||||
|
TransitionWireImage(source, sourceLayout);
|
||||||
|
if (sameImage) destination.layout = source.layout;
|
||||||
|
else TransitionWireImage(destination, destinationLayout);
|
||||||
const auto makeView = [&](const WireImage& image) {
|
const auto makeView = [&](const WireImage& image) {
|
||||||
VkImageViewCreateInfo view{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
|
VkImageViewCreateInfo view{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
|
||||||
view.image = image.image;
|
view.image = image.image;
|
||||||
@@ -213,7 +235,7 @@
|
|||||||
alloc.pSetLayouts = &cache.descriptorLayout;
|
alloc.pSetLayouts = &cache.descriptorLayout;
|
||||||
VK_VERIFY(vkAllocateDescriptorSets(m_device, &alloc, &descriptorSet));
|
VK_VERIFY(vkAllocateDescriptorSets(m_device, &alloc, &descriptorSet));
|
||||||
const VkDescriptorImageInfo sampled{filter == GL_LINEAR ? cache.linear : cache.nearest,
|
const VkDescriptorImageInfo sampled{filter == GL_LINEAR ? cache.linear : cache.nearest,
|
||||||
sourceView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL};
|
sourceView, sourceLayout};
|
||||||
VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
|
VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
|
||||||
write.dstSet = descriptorSet;
|
write.dstSet = descriptorSet;
|
||||||
write.dstBinding = 0;
|
write.dstBinding = 0;
|
||||||
@@ -227,6 +249,12 @@
|
|||||||
transform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
|
transform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
|
||||||
const float dw = static_cast<float>(quarterTurn ? destination.extent.height : destination.extent.width);
|
const float dw = static_cast<float>(quarterTurn ? destination.extent.height : destination.extent.width);
|
||||||
const float dh = static_cast<float>(quarterTurn ? destination.extent.width : destination.extent.height);
|
const float dh = static_cast<float>(quarterTurn ? destination.extent.width : destination.extent.height);
|
||||||
|
// The shared vertex shader flips window Y. Offscreen images keep the
|
||||||
|
// source row order, so undo that flip in the destination rectangle.
|
||||||
|
if (!destination.isDefault) {
|
||||||
|
dy0 = static_cast<GLint>(destination.extent.height) - dy0;
|
||||||
|
dy1 = static_cast<GLint>(destination.extent.height) - dy1;
|
||||||
|
}
|
||||||
const Constants constants{{sx0 / sw, sy0 / sh, (static_cast<float>(sx1) - sx0) / sw,
|
const Constants constants{{sx0 / sw, sy0 / sh, (static_cast<float>(sx1) - sx0) / sw,
|
||||||
(static_cast<float>(sy1) - sy0) / sh},
|
(static_cast<float>(sy1) - sy0) / sh},
|
||||||
{dx0 / dw, dy0 / dh, (static_cast<float>(dx1) - dx0) / dw,
|
{dx0 / dw, dy0 / dh, (static_cast<float>(dx1) - dx0) / dw,
|
||||||
@@ -260,7 +288,7 @@
|
|||||||
retired.imageViews = {sourceView, destinationView};
|
retired.imageViews = {sourceView, destinationView};
|
||||||
retired.descriptorPools.push_back(descriptorPool);
|
retired.descriptorPools.push_back(descriptorPool);
|
||||||
RetireWireObjects(Move(retired));
|
RetireWireObjects(Move(retired));
|
||||||
m_swapchainObject.SetImageContentDefined(m_imageIndexAcquired, true);
|
if (destination.isDefault) m_swapchainObject.SetImageContentDefined(m_imageIndexAcquired, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -661,8 +661,9 @@
|
|||||||
RetireWireDrawPass();
|
RetireWireDrawPass();
|
||||||
auto& state = MG_Pipe::MGPipeApplier();
|
auto& state = MG_Pipe::MGPipeApplier();
|
||||||
Uint32 base = state.VerbMipBaseLevel, baseLayer = 0, layerCount = 1;
|
Uint32 base = state.VerbMipBaseLevel, baseLayer = 0, layerCount = 1;
|
||||||
|
VkFormat viewFormat = VK_FORMAT_UNDEFINED;
|
||||||
const auto handle = m_textureManager->ResolveWireTextureStorage(state.VerbMipRes, base, baseLayer,
|
const auto handle = m_textureManager->ResolveWireTextureStorage(state.VerbMipRes, base, baseLayer,
|
||||||
nullptr, &layerCount);
|
&viewFormat, &layerCount);
|
||||||
auto* resource = m_textureManager->SyncTextureResourceByHandle(handle);
|
auto* resource = m_textureManager->SyncTextureResourceByHandle(handle);
|
||||||
if (!resource) MagmaWireFatal("mipmap-resource");
|
if (!resource) MagmaWireFatal("mipmap-resource");
|
||||||
m_textureManager->FlushPendingUploads();
|
m_textureManager->FlushPendingUploads();
|
||||||
@@ -676,38 +677,74 @@
|
|||||||
if (resource->aspect != VK_IMAGE_ASPECT_COLOR_BIT && resource->aspect != VK_IMAGE_ASPECT_DEPTH_BIT)
|
if (resource->aspect != VK_IMAGE_ASPECT_COLOR_BIT && resource->aspect != VK_IMAGE_ASPECT_DEPTH_BIT)
|
||||||
MagmaWireFatal("depth-stencil-mipmap@P7");
|
MagmaWireFatal("depth-stencil-mipmap@P7");
|
||||||
VkFormatProperties props{};
|
VkFormatProperties props{};
|
||||||
vkGetPhysicalDeviceFormatProperties(m_physicalDevice.handle,resource->format,&props);
|
const VkFormat format = viewFormat == VK_FORMAT_UNDEFINED ? resource->format : viewFormat;
|
||||||
|
vkGetPhysicalDeviceFormatProperties(m_physicalDevice.handle,format,&props);
|
||||||
const auto required = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
|
const auto required = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
|
||||||
if ((props.optimalTilingFeatures & required) != required) MagmaWireFatal("mipmap-native-format@P7");
|
const Bool nativeBlit = format == resource->format && (props.optimalTilingFeatures & required) == required;
|
||||||
const VkFilter filter = props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT ?
|
const VkFilter filter = props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT ?
|
||||||
VK_FILTER_LINEAR : VK_FILTER_NEAREST;
|
VK_FILTER_LINEAR : VK_FILTER_NEAREST;
|
||||||
VkPipelineStageFlags stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
if (!nativeBlit) {
|
||||||
VkAccessFlags access = 0;
|
// Packed floating-point color formats may support sampling and
|
||||||
GetImageTransitionSourceState(resource->layout,stage,access);
|
// rendering without BLIT_DST. Generate from actual GPU mip contents
|
||||||
if (!VkTextureManager::TransitionImageLayout(frame.commandBuffer,resource->image,resource->layout,
|
// through the native shader pass; no client or staged texels enter.
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,stage,VK_PIPELINE_STAGE_TRANSFER_BIT,access,
|
if (resource->aspect != VK_IMAGE_ASPECT_COLOR_BIT ||
|
||||||
VK_ACCESS_TRANSFER_WRITE_BIT,resource->aspect,0,resource->mipLevels)) MagmaWireFatal("mipmap-transition");
|
resource->viewType == VK_IMAGE_VIEW_TYPE_1D || resource->viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY ||
|
||||||
for (Uint32 level=base+1;level<base+count;++level) {
|
resource->viewType == VK_IMAGE_VIEW_TYPE_3D ||
|
||||||
VkImageLayout previous = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
!(resource->usageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) ||
|
||||||
VkTextureManager::TransitionImageLayout(frame.commandBuffer,resource->image,previous,
|
baseLayer >= resource->arrayLayers || layerCount > resource->arrayLayers - baseLayer)
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,VK_PIPELINE_STAGE_TRANSFER_BIT,VK_PIPELINE_STAGE_TRANSFER_BIT,
|
MagmaWireFatal("mipmap-shader-format-or-shape");
|
||||||
VK_ACCESS_TRANSFER_WRITE_BIT,VK_ACCESS_TRANSFER_READ_BIT,resource->aspect,level-1,1);
|
for (Uint32 level = base + 1; level < base + count; ++level) {
|
||||||
VkImageBlit blit{};
|
for (Uint32 layer = baseLayer; layer < baseLayer + layerCount; ++layer) {
|
||||||
blit.srcSubresource = {resource->aspect,level-1,baseLayer,layerCount};
|
WireImage source{};
|
||||||
blit.dstSubresource = {resource->aspect,level,baseLayer,layerCount};
|
source.image = resource->image;
|
||||||
blit.srcOffsets[1] = {static_cast<Int32>(std::max(resource->extent.width>>(level-1),1u)),
|
source.format = format;
|
||||||
static_cast<Int32>(std::max(resource->extent.height>>(level-1),1u)),
|
source.aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
static_cast<Int32>(std::max(resource->depth>>(level-1),1u))};
|
source.layout = resource->layout;
|
||||||
blit.dstOffsets[1] = {static_cast<Int32>(std::max(resource->extent.width>>level,1u)),
|
source.trackedLayout = &resource->layout;
|
||||||
static_cast<Int32>(std::max(resource->extent.height>>level,1u)),
|
source.levels = resource->mipLevels;
|
||||||
static_cast<Int32>(std::max(resource->depth>>level,1u))};
|
source.level = level - 1;
|
||||||
vkCmdBlitImage(frame.commandBuffer,resource->image,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
source.layer = layer;
|
||||||
resource->image,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,1,&blit,filter);
|
source.extent = {std::max(resource->extent.width >> (level - 1), 1u),
|
||||||
VkTextureManager::TransitionImageLayout(frame.commandBuffer,resource->image,previous,
|
std::max(resource->extent.height >> (level - 1), 1u)};
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,VK_PIPELINE_STAGE_TRANSFER_BIT,VK_PIPELINE_STAGE_TRANSFER_BIT,
|
WireImage destination = source;
|
||||||
VK_ACCESS_TRANSFER_READ_BIT,VK_ACCESS_TRANSFER_WRITE_BIT,resource->aspect,level-1,1);
|
destination.level = level;
|
||||||
|
destination.extent = {std::max(resource->extent.width >> level, 1u),
|
||||||
|
std::max(resource->extent.height >> level, 1u)};
|
||||||
|
if (!BlitWireColorImage(source, destination, 0, 0, source.extent.width, source.extent.height,
|
||||||
|
0, 0, destination.extent.width, destination.extent.height,
|
||||||
|
filter == VK_FILTER_LINEAR ? GL_LINEAR : GL_NEAREST, true))
|
||||||
|
MagmaWireFatal("mipmap-shader-blit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VkPipelineStageFlags stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
VkAccessFlags access = 0;
|
||||||
|
GetImageTransitionSourceState(resource->layout,stage,access);
|
||||||
|
if (!VkTextureManager::TransitionImageLayout(frame.commandBuffer,resource->image,resource->layout,
|
||||||
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,stage,VK_PIPELINE_STAGE_TRANSFER_BIT,access,
|
||||||
|
VK_ACCESS_TRANSFER_WRITE_BIT,resource->aspect,0,resource->mipLevels)) MagmaWireFatal("mipmap-transition");
|
||||||
|
for (Uint32 level=base+1;level<base+count;++level) {
|
||||||
|
VkImageLayout previous = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
VkTextureManager::TransitionImageLayout(frame.commandBuffer,resource->image,previous,
|
||||||
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,VK_PIPELINE_STAGE_TRANSFER_BIT,VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_ACCESS_TRANSFER_WRITE_BIT,VK_ACCESS_TRANSFER_READ_BIT,resource->aspect,level-1,1);
|
||||||
|
VkImageBlit blit{};
|
||||||
|
blit.srcSubresource = {resource->aspect,level-1,baseLayer,layerCount};
|
||||||
|
blit.dstSubresource = {resource->aspect,level,baseLayer,layerCount};
|
||||||
|
blit.srcOffsets[1] = {static_cast<Int32>(std::max(resource->extent.width>>(level-1),1u)),
|
||||||
|
static_cast<Int32>(std::max(resource->extent.height>>(level-1),1u)),
|
||||||
|
static_cast<Int32>(std::max(resource->depth>>(level-1),1u))};
|
||||||
|
blit.dstOffsets[1] = {static_cast<Int32>(std::max(resource->extent.width>>level,1u)),
|
||||||
|
static_cast<Int32>(std::max(resource->extent.height>>level,1u)),
|
||||||
|
static_cast<Int32>(std::max(resource->depth>>level,1u))};
|
||||||
|
vkCmdBlitImage(frame.commandBuffer,resource->image,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
resource->image,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,1,&blit,filter);
|
||||||
|
VkTextureManager::TransitionImageLayout(frame.commandBuffer,resource->image,previous,
|
||||||
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,VK_PIPELINE_STAGE_TRANSFER_BIT,VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_ACCESS_TRANSFER_READ_BIT,VK_ACCESS_TRANSFER_WRITE_BIT,resource->aspect,level-1,1);
|
||||||
|
}
|
||||||
|
resource->layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
}
|
}
|
||||||
resource->layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
||||||
auto& record = state.TextureResources[handle.Slot];
|
auto& record = state.TextureResources[handle.Slot];
|
||||||
record.Desc.Levels = std::max<Uint16>(record.Desc.Levels,static_cast<Uint16>(base+count));
|
record.Desc.Levels = std::max<Uint16>(record.Desc.Levels,static_cast<Uint16>(base+count));
|
||||||
resource->syncedWireSerial = ++record.Serial;
|
resource->syncedWireSerial = ++record.Serial;
|
||||||
|
|||||||
@@ -460,6 +460,25 @@ TEST_F(F1WireScenario, GenerateMipmapPackedFloatPixels) {
|
|||||||
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR));
|
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR));
|
||||||
const GLfloat expected[4] = {0.25f, 0.5f, 0.75f, 1.0f};
|
const GLfloat expected[4] = {0.25f, 0.5f, 0.75f, 1.0f};
|
||||||
for (int i = 0; i < 4; ++i) EXPECT_NEAR(pixel[i], expected[i], 0.01f);
|
for (int i = 0; i < 4; ++i) EXPECT_NEAR(pixel[i], expected[i], 0.01f);
|
||||||
|
|
||||||
|
// Change the base on the GPU after its client upload. The regenerated mip
|
||||||
|
// must read those new pixels, and offscreen shader blits must keep row order.
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glClearColor(0.875f, 0.375f, 0.125f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
glScissor(0, 4, 8, 4);
|
||||||
|
glClearColor(0.125f, 0.75f, 0.5f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 2);
|
||||||
|
GLfloat rows[8]{};
|
||||||
|
glReadPixels(1, 0, 1, 2, GL_RGBA, GL_FLOAT, rows);
|
||||||
|
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR));
|
||||||
|
const GLfloat generated[8] = {0.875f, 0.375f, 0.125f, 1.0f, 0.125f, 0.75f, 0.5f, 1.0f};
|
||||||
|
for (int i = 0; i < 8; ++i) EXPECT_NEAR(rows[i], generated[i], 0.01f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(F1WireScenario, GenerateMipmapDepthPixels) {
|
TEST_F(F1WireScenario, GenerateMipmapDepthPixels) {
|
||||||
|
|||||||
Reference in New Issue
Block a user