mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Fix] (DirectVulkan): back a 1D array with its layers in arrayLayers, not in the image height
This commit is contained in:
@@ -1636,6 +1636,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return false;
|
||||
}
|
||||
|
||||
// From here down the size is VULKAN geometry, not GL's: a 1D array's layer count moves
|
||||
// out of the height it occupies GL-side and into z, which is the slot
|
||||
// TryResolveTextureShapeInfo reads arrayLayers from and the only one that leaves
|
||||
// extent.height at the 1 a VK_IMAGE_TYPE_1D image is required to have.
|
||||
texelSize = ToVulkanLevelExtent(texture.GetTarget(), texelSize);
|
||||
|
||||
if (!SyncTextureResource(texture, uploadTarget, texelSize, byteSize, mipLevelCount, outResource)) {
|
||||
MGLOG_D("%s: SyncTextureResource failed", __func__);
|
||||
return false;
|
||||
@@ -2545,7 +2551,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
uploadItem.target = target;
|
||||
uploadItem.level = level;
|
||||
uploadItem.baseArrayLayer = ResolveUploadArrayLayer(target);
|
||||
uploadItem.texelSize = texelSize;
|
||||
// Vulkan geometry, like the image this stages into (see SyncTexture): a 1D
|
||||
// array's layers move from y to z, where the copy loop's depthSelectsArrayLayer
|
||||
// branch turns them into layerCount. The shadow needs no repacking to follow -
|
||||
// one layer of a 1D array IS one row of `width` texels, so the tight-packed
|
||||
// per-layer copy the swapped size describes reads the same bytes in the same
|
||||
// order as the row-major level it replaces.
|
||||
uploadItem.texelSize = ToVulkanLevelExtent(mipmapTexture.GetTarget(), texelSize);
|
||||
uploadItem.source = source;
|
||||
uploadItem.offset = stagingSize;
|
||||
uploadItem.uploadByteSize = byteSize;
|
||||
@@ -2583,6 +2595,23 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
uploadItem.uploadByteSize = rectTexels * uploadItem.texelBytes;
|
||||
}
|
||||
// The boxes came out of the shadow in GL coordinates, where a 1D
|
||||
// array's layer is the y. They have to follow texelSize across to z or
|
||||
// they would address rows of an image that now has exactly one, and
|
||||
// the staging walk would read the wrong bytes for them. Every byte
|
||||
// count computed above is a product of the three extents, so moving
|
||||
// the axes leaves all of them alone - and an OFFSET lands on a zero y,
|
||||
// not on the extent's one, which is why this is spelled out rather than
|
||||
// handed to ToVulkanLevelExtent.
|
||||
if (mipmapTexture.GetTarget() == TextureTarget::Texture1DArray) {
|
||||
uploadItem.regionLo = {uploadItem.regionLo.x(), 0, uploadItem.regionLo.y()};
|
||||
uploadItem.regionSize = {uploadItem.regionSize.x(), 1,
|
||||
uploadItem.regionSize.y()};
|
||||
for (auto& rect : uploadItem.rects) {
|
||||
rect.lo = {rect.lo.x(), 0, rect.lo.y()};
|
||||
rect.hi = {rect.hi.x(), 1, rect.hi.y()};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (formatInfo.expandRgbToRgba) {
|
||||
|
||||
@@ -22,6 +22,25 @@ class ITextureObject;
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
enum class SamplerNumericDomain : Uint8;
|
||||
|
||||
// A GL 1D-ARRAY level keeps its LAYER COUNT in the state-side HEIGHT: that is what
|
||||
// glTexImage2D(GL_TEXTURE_1D_ARRAY, width, layers) means, and the frontend records the level
|
||||
// as {width, layers, 1} (see GL_Texture.cpp's AllocateStorage and the completeness walk in
|
||||
// TextureObject.cpp, which shrinks only x down the chain). Vulkan packs it the other way: a
|
||||
// 1D array is a VK_IMAGE_TYPE_1D image whose extent.height MUST be 1 and whose layers live in
|
||||
// arrayLayers - i.e. in the slot this backend reads out of z. So every place that turns a GL
|
||||
// level size into Vulkan image geometry has to move the count across first, and every GL-space
|
||||
// sub-box that rides along with it has to move its y the same way. DirectGLES performs the
|
||||
// identical remap onto the ES 2D array it maps 1D arrays to (GetBackendUploadSize).
|
||||
//
|
||||
// Applied to nothing else: a 2D array, a cube array and a 3D texture all already carry their
|
||||
// depth/layer count in z, which is where the Vulkan side expects it.
|
||||
inline IntVec3 ToVulkanLevelExtent(TextureTarget stateTarget, const IntVec3& glTexelSize) {
|
||||
if (stateTarget == TextureTarget::Texture1DArray) {
|
||||
return {glTexelSize.x(), 1, glTexelSize.y()};
|
||||
}
|
||||
return glTexelSize;
|
||||
}
|
||||
|
||||
class VkTextureManager {
|
||||
public:
|
||||
// Monotonic epoch bumped whenever a texture VkImage is (re)created. The render-pass
|
||||
|
||||
@@ -9813,7 +9813,7 @@ void main() {
|
||||
VkImageAspectFlags imageAspect, Uint32 mipLevel,
|
||||
Uint32 baseArrayLayer, GLint x, GLint y, GLsizei width,
|
||||
GLsizei height, GLenum format, GLenum type, void* pixels,
|
||||
Bool defaultFramebufferOrientation) {
|
||||
Bool defaultFramebufferOrientation, Uint32 sourceLayerCount) {
|
||||
const Bool wantDepth = format != GL_STENCIL_INDEX;
|
||||
const Bool wantStencil = format != GL_DEPTH_COMPONENT;
|
||||
auto& frame = m_frameContext.GetCurrent();
|
||||
@@ -9892,6 +9892,10 @@ void main() {
|
||||
if (!mapped) return;
|
||||
}
|
||||
|
||||
// See the header: a stack of one-row layers and a single multi-row layer copy out to the
|
||||
// same tightly-packed bytes, so only the region's shape splits the two cases.
|
||||
const Uint32 copyLayerCount = std::max<Uint32>(sourceLayerCount, 1u);
|
||||
const Uint32 copyRowCount = copyLayerCount > 1u ? 1u : copyExtent.height;
|
||||
VkBufferImageCopy regions[2]{};
|
||||
Uint32 regionCount = 0;
|
||||
if (wantDepth) {
|
||||
@@ -9900,9 +9904,9 @@ void main() {
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
region.imageSubresource.mipLevel = mipLevel;
|
||||
region.imageSubresource.baseArrayLayer = baseArrayLayer;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
region.imageSubresource.layerCount = copyLayerCount;
|
||||
region.imageOffset = {copyOffset.x, copyOffset.y, 0};
|
||||
region.imageExtent = {copyExtent.width, copyExtent.height, 1};
|
||||
region.imageExtent = {copyExtent.width, copyRowCount, 1};
|
||||
}
|
||||
if (wantStencil) {
|
||||
auto& region = regions[regionCount++];
|
||||
@@ -9910,9 +9914,9 @@ void main() {
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
region.imageSubresource.mipLevel = mipLevel;
|
||||
region.imageSubresource.baseArrayLayer = baseArrayLayer;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
region.imageSubresource.layerCount = copyLayerCount;
|
||||
region.imageOffset = {copyOffset.x, copyOffset.y, 0};
|
||||
region.imageExtent = {copyExtent.width, copyExtent.height, 1};
|
||||
region.imageExtent = {copyExtent.width, copyRowCount, 1};
|
||||
}
|
||||
vkCmdCopyImageToBuffer(frame.commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, readback.GetHandle(),
|
||||
regionCount, regions);
|
||||
@@ -10147,9 +10151,17 @@ void main() {
|
||||
? static_cast<Uint32>(textureUploadTarget) -
|
||||
static_cast<Uint32>(TextureUploadTarget::CubeMapPositiveX)
|
||||
: 0;
|
||||
// A 1D array's levelSize.y() is its LAYER count, and those layers are the rows
|
||||
// GL wants back - but in Vulkan they are array layers of a one-row image, not
|
||||
// rows of layer 0, so the read has to be told which of the two it is looking at.
|
||||
const Uint32 sourceLayers =
|
||||
textureObject->GetTarget() == TextureTarget::Texture1DArray
|
||||
? static_cast<Uint32>(std::max<Int>(levelSize.y(), 1))
|
||||
: 1u;
|
||||
ReadDepthStencilImageToClient(resource->image, resource->format, &resource->layout, resource->aspect,
|
||||
static_cast<Uint32>(level), arrayLayer, 0, 0, levelSize.x(),
|
||||
levelSize.y(), format, type, pixels);
|
||||
levelSize.y(), format, type, pixels,
|
||||
/*defaultFramebufferOrientation=*/false, sourceLayers);
|
||||
} else {
|
||||
MGLOG_E_ONCE("DirectVulkan::GetTexImage skipped: color query of a non-color texture");
|
||||
}
|
||||
@@ -10167,12 +10179,19 @@ void main() {
|
||||
// destination layout (GL 3.3 section 6.1.4).
|
||||
const auto imageTextureTarget = textureObject->GetTarget();
|
||||
const Bool is3dImage = imageTextureTarget == TextureTarget::Texture3D;
|
||||
const Bool isArrayImage = imageTextureTarget == TextureTarget::Texture1DArray ||
|
||||
const Bool is1dArrayImage = imageTextureTarget == TextureTarget::Texture1DArray;
|
||||
const Bool isArrayImage = is1dArrayImage ||
|
||||
imageTextureTarget == TextureTarget::Texture2DArray ||
|
||||
imageTextureTarget == TextureTarget::TextureCubeMapArray;
|
||||
const GLsizei depthSlices = is3dImage ? std::max<GLsizei>(texelSize.z(), 1) : 1;
|
||||
const GLsizei arrayLayers = isArrayImage ? static_cast<GLsizei>(resource->arrayLayers) : 1;
|
||||
const GLsizei sliceCount = std::max<GLsizei>(depthSlices * arrayLayers, 1);
|
||||
// A 1D array level comes back as ONE two-dimensional image whose rows are its layers
|
||||
// (GL 4.6 core 8.11.4), so its layers are already counted by `height` above and must not
|
||||
// multiply the slice count the way a 2D-array's or a cube-array's do. Vulkan still keeps
|
||||
// them in arrayLayers on a one-row image, which is what the copy region below says - the
|
||||
// two describe the same tightly-packed bytes.
|
||||
const GLsizei sliceCount =
|
||||
std::max<GLsizei>(depthSlices * (is1dArrayImage ? 1 : arrayLayers), 1);
|
||||
if (bufSize >= 0) {
|
||||
const Int dstChannels = GetReadbackChannelCount(format);
|
||||
if ((type == GL_UNSIGNED_BYTE || type == GL_FLOAT) && dstChannels > 0) {
|
||||
@@ -10225,7 +10244,8 @@ void main() {
|
||||
copyRegion.imageSubresource.mipLevel = static_cast<Uint32>(level);
|
||||
copyRegion.imageSubresource.baseArrayLayer = 0;
|
||||
copyRegion.imageSubresource.layerCount = static_cast<Uint32>(arrayLayers);
|
||||
copyRegion.imageExtent = {static_cast<Uint32>(width), static_cast<Uint32>(height),
|
||||
copyRegion.imageExtent = {static_cast<Uint32>(width),
|
||||
is1dArrayImage ? 1u : static_cast<Uint32>(height),
|
||||
static_cast<Uint32>(depthSlices)};
|
||||
vkCmdCopyImageToBuffer(frame.commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
readback.GetHandle(), 1, ©Region);
|
||||
|
||||
@@ -217,10 +217,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// depth/stencil image, which this renderer stores display-side-up: the copy rect then
|
||||
// has to be mapped out of GL's bottom-origin space and the copied rows re-oriented on
|
||||
// the way back, exactly as the colour ReadPixels path does.
|
||||
// `sourceLayerCount` above 1 says the `height` rows the client is owed are stored as that
|
||||
// many ARRAY LAYERS of a one-row image rather than as rows of one layer - the shape a GL
|
||||
// 1D array has in Vulkan. The two produce byte-identical tightly-packed readbacks, so
|
||||
// only the copy region differs; everything after it is written against `height`.
|
||||
void ReadDepthStencilImageToClient(VkImage image, VkFormat vkFormat, VkImageLayout* trackedLayout,
|
||||
VkImageAspectFlags imageAspect, Uint32 mipLevel, Uint32 baseArrayLayer,
|
||||
GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
|
||||
void* pixels, Bool defaultFramebufferOrientation = false);
|
||||
void* pixels, Bool defaultFramebufferOrientation = false,
|
||||
Uint32 sourceLayerCount = 1);
|
||||
// Same-extent depth blit between images of different depth formats: host
|
||||
// round-trip with a per-texel re-encode (see BlitNamedFramebuffer).
|
||||
Bool BlitDepthAcrossFormats(FrameContext::FrameData& frame, VkImage srcImage, VkFormat srcFormat,
|
||||
|
||||
Reference in New Issue
Block a user