Vulkan: Have a cubemap as 2D-array view handy Previously, only texture copies used a shader that performed texelFetch(). To support cubemaps, a hack was used to temporarily create a 2D array view. With upcoming support for multisample resolve, more shaders will be using texelFetch() all requiring this workaround. This change instead makes sure that a separate view is created for cubemaps for the purpose of being used with these shaders. As a result, we have three logical views on textures and render targets: - Draw: a view that can be used as a color/depth/stencil attachment - Read: a view that can be used to sample from - Fetch: a view that can be used to fetch from The fetch view is generally the same as the read view, except for cube maps. Bug: angleproject:3200 Change-Id: I21547f728c16f0aa8f0fcae152c400b5cc1565da Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1628585 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp b/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp index 9a00f2a..01b579c 100644 --- a/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp +++ b/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp
@@ -18,7 +18,11 @@ namespace rx { RenderTargetVk::RenderTargetVk() - : mImage(nullptr), mImageView(nullptr), mLevelIndex(0), mLayerIndex(0) + : mImage(nullptr), + mImageView(nullptr), + mCubeImageFetchView(nullptr), + mLevelIndex(0), + mLayerIndex(0) {} RenderTargetVk::~RenderTargetVk() {} @@ -26,17 +30,20 @@ RenderTargetVk::RenderTargetVk(RenderTargetVk &&other) : mImage(other.mImage), mImageView(other.mImageView), + mCubeImageFetchView(other.mCubeImageFetchView), mLevelIndex(other.mLevelIndex), mLayerIndex(other.mLayerIndex) {} void RenderTargetVk::init(vk::ImageHelper *image, vk::ImageView *imageView, + vk::ImageView *cubeImageFetchView, size_t levelIndex, size_t layerIndex) { mImage = image; mImageView = imageView; + mCubeImageFetchView = cubeImageFetchView; mLevelIndex = levelIndex; mLayerIndex = layerIndex; } @@ -45,6 +52,7 @@ { mImage = nullptr; mImageView = nullptr; + mCubeImageFetchView = nullptr; mLevelIndex = 0; mLayerIndex = 0; } @@ -108,6 +116,12 @@ return getDrawImageView(); } +vk::ImageView *RenderTargetVk::getFetchImageView() const +{ + return mCubeImageFetchView && mCubeImageFetchView->valid() ? mCubeImageFetchView + : getReadImageView(); +} + const vk::Format &RenderTargetVk::getImageFormat() const { ASSERT(mImage && mImage->valid()); @@ -125,6 +139,7 @@ ASSERT(image && image->valid() && imageView && imageView->valid()); mImage = image; mImageView = imageView; + mCubeImageFetchView = nullptr; } vk::ImageHelper *RenderTargetVk::getImageForRead(vk::CommandGraphResource *readingResource,
diff --git a/src/libANGLE/renderer/vulkan/RenderTargetVk.h b/src/libANGLE/renderer/vulkan/RenderTargetVk.h index 4bfda37..9c9cd36 100644 --- a/src/libANGLE/renderer/vulkan/RenderTargetVk.h +++ b/src/libANGLE/renderer/vulkan/RenderTargetVk.h
@@ -45,6 +45,7 @@ void init(vk::ImageHelper *image, vk::ImageView *imageView, + vk::ImageView *cubeImageFetchView, size_t levelIndex, size_t layerIndex); void reset(); @@ -68,6 +69,10 @@ vk::ImageView *getDrawImageView() const; vk::ImageView *getReadImageView() const; + // GLSL's texelFetch() needs a 2D array view to read from cube maps. This function returns the + // same view as `getReadImageView()`, except for cubemaps, in which case it returns a 2D array + // view of it. + vk::ImageView *getFetchImageView() const; const vk::Format &getImageFormat() const; gl::Extents getExtents() const; @@ -85,6 +90,8 @@ // Note that the draw and read image views are the same, given the requirements of a render // target. vk::ImageView *mImageView; + // For cubemaps, a 2D-array view is also created to be used with shaders that use texelFetch(). + vk::ImageView *mCubeImageFetchView; size_t mLevelIndex; size_t mLayerIndex; };
diff --git a/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp b/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp index 53e1a5f..0850a08 100644 --- a/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp +++ b/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp
@@ -87,7 +87,7 @@ // Clear the renderbuffer if it has emulated channels. mImage->clearIfEmulatedFormat(vk::GetImpl(context), gl::ImageIndex::Make2D(0), vkFormat); - mRenderTarget.init(mImage, &mImageView, 0, 0); + mRenderTarget.init(mImage, &mImageView, nullptr, 0, 0); } return angle::Result::Continue; @@ -134,7 +134,19 @@ gl::SwizzleState(), &mImageView, imageVk->getImageLevel(), 1, imageVk->getImageLayer(), 1)); - mRenderTarget.init(mImage, &mImageView, imageVk->getImageLevel(), imageVk->getImageLayer()); + if (imageVk->getImageTextureType() == gl::TextureType::CubeMap) + { + gl::TextureType arrayType = imageVk->getImage()->getSamples() > 1 + ? gl::TextureType::_2DMultisampleArray + : gl::TextureType::_2DArray; + ANGLE_TRY(mImage->initLayerImageView(contextVk, arrayType, aspect, gl::SwizzleState(), + &mCubeImageFetchView, imageVk->getImageLevel(), 1, + imageVk->getImageLayer(), 1)); + } + + mRenderTarget.init(mImage, &mImageView, + mCubeImageFetchView.valid() ? &mCubeImageFetchView : nullptr, + imageVk->getImageLevel(), imageVk->getImageLayer()); return angle::Result::Continue; } @@ -185,6 +197,7 @@ } renderer->releaseObject(renderer->getCurrentQueueSerial(), &mImageView); + renderer->releaseObject(renderer->getCurrentQueueSerial(), &mCubeImageFetchView); } } // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/RenderbufferVk.h b/src/libANGLE/renderer/vulkan/RenderbufferVk.h index 57c6ccc..306eebf 100644 --- a/src/libANGLE/renderer/vulkan/RenderbufferVk.h +++ b/src/libANGLE/renderer/vulkan/RenderbufferVk.h
@@ -54,6 +54,7 @@ bool mOwnsImage; vk::ImageHelper *mImage; vk::ImageView mImageView; + vk::ImageView mCubeImageFetchView; RenderTargetVk mRenderTarget; };
diff --git a/src/libANGLE/renderer/vulkan/SurfaceVk.cpp b/src/libANGLE/renderer/vulkan/SurfaceVk.cpp index 01d90a0..59a7420 100644 --- a/src/libANGLE/renderer/vulkan/SurfaceVk.cpp +++ b/src/libANGLE/renderer/vulkan/SurfaceVk.cpp
@@ -163,9 +163,9 @@ EGLint height) : SurfaceVk(surfaceState), mWidth(width), mHeight(height) { - mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageView, 0, 0); + mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageView, nullptr, 0, 0); mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image, - &mDepthStencilAttachment.imageView, 0, 0); + &mDepthStencilAttachment.imageView, nullptr, 0, 0); } OffscreenSurfaceVk::~OffscreenSurfaceVk() {} @@ -374,8 +374,8 @@ { // Initialize the color render target with the multisampled targets. If not multisampled, the // render target will be updated to refer to a swapchain image on every acquire. - mColorRenderTarget.init(&mColorImageMS, &mColorImageViewMS, 0, 0); - mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageView, 0, 0); + mColorRenderTarget.init(&mColorImageMS, &mColorImageViewMS, nullptr, 0, 0); + mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageView, nullptr, 0, 0); } WindowSurfaceVk::~WindowSurfaceVk()
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp index 2069c1d..4227b85 100644 --- a/src/libANGLE/renderer/vulkan/TextureVk.cpp +++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
@@ -426,9 +426,8 @@ ASSERT(offsetImageIndex.getLayerCount() == 1); return copySubImageImplWithDraw(contextVk, offsetImageIndex, modifiedDestOffset, destFormat, - 0, colorReadRT->getLayerIndex(), clippedSourceArea, - isViewportFlipY, false, false, false, - &colorReadRT->getImage(), colorReadRT->getReadImageView()); + 0, clippedSourceArea, isViewportFlipY, false, false, false, + &colorReadRT->getImage(), colorReadRT->getFetchImageView()); } // Do a CPU readback that does the conversion, and then stage the change to the pixel buffer. @@ -476,9 +475,9 @@ if (CanCopyWithDraw(renderer, sourceVkFormat, destVkFormat) && !forceCpuPath) { return copySubImageImplWithDraw(contextVk, offsetImageIndex, destOffset, destVkFormat, - sourceLevel, 0, sourceArea, false, unpackFlipY, + sourceLevel, sourceArea, false, unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha, - &source->getImage(), &source->getReadImageView()); + &source->getImage(), &source->getFetchImageView()); } if (sourceLevel != 0) @@ -626,7 +625,6 @@ const gl::Offset &destOffset, const vk::Format &destFormat, size_t sourceLevel, - size_t sourceLayer, const gl::Rectangle &sourceArea, bool isSrcFlipY, bool unpackFlipY, @@ -657,33 +655,6 @@ uint32_t baseLayer = index.hasLayer() ? index.getLayerIndex() : 0; uint32_t layerCount = index.getLayerCount(); - // If the source image is a cube map, the view is of VK_IMAGE_VIEW_TYPE_CUBE type. However, - // GLSL's texelFetch cannot take a textureCube. We need to create a 2D_ARRAY type view to be - // able to perform the copy. - // - // Note(syoussefi): Array textures are not yet supported in Vulkan. Once they are, the if below - // should be changed to detect cube maps only. - vk::ImageView cubeAs2DArrayView; - if (srcImage->getLayerCount() % 6 == 0) - { - // TODO(syoussefi): If the cube map is LUMA, we need swizzle. http://anglebug.com/2911 - // This can't happen when copying from framebuffers, so only source of concern would be - // copy[Sub]Texture copying from a LUMA cube map. - ASSERT(!srcImage->getFormat().imageFormat().isLUMA()); - - gl::TextureType arrayTextureType = - Get2DTextureType(srcImage->getLayerCount(), srcImage->getSamples()); - ANGLE_TRY(srcImage->initImageView(contextVk, arrayTextureType, VK_IMAGE_ASPECT_COLOR_BIT, - gl::SwizzleState(), &cubeAs2DArrayView, 0, - srcImage->getLevelCount())); - srcView = &cubeAs2DArrayView; - } - else - { - // Source layer is otherwise baked into the view - sourceLayer = 0; - } - // If destination is valid, copy the source directly into it. if (mImage->valid()) { @@ -692,7 +663,7 @@ for (uint32_t layerIndex = 0; layerIndex < layerCount; ++layerIndex) { - params.srcLayer = sourceLayer + layerIndex; + params.srcLayer = layerIndex; vk::ImageView *destView; ANGLE_TRY( @@ -720,7 +691,7 @@ for (uint32_t layerIndex = 0; layerIndex < layerCount; ++layerIndex) { - params.srcLayer = sourceLayer + layerIndex; + params.srcLayer = layerIndex; // Create a temporary view for this layer. vk::ImageView stagingView; @@ -742,11 +713,6 @@ gl::Extents(sourceArea.width, sourceArea.height, 1)); } - if (cubeAs2DArrayView.valid()) - { - renderer->releaseObject(currentQueueSerial, &cubeAs2DArrayView); - } - return angle::Result::Continue; } @@ -928,8 +894,8 @@ mImage = imageHelper; mImage->initStagingBuffer(renderer, format); - mRenderTarget.init(mImage, &mDrawBaseLevelImageView, getNativeImageLevel(0), - getNativeImageLayer(0)); + mRenderTarget.init(mImage, &mDrawBaseLevelImageView, &mFetchBaseLevelImageView, + getNativeImageLevel(0), getNativeImageLayer(0)); // Force re-creation of cube map render targets next time they are needed mCubeMapRenderTargets.clear(); @@ -1208,13 +1174,26 @@ if (!mCubeMapRenderTargets.empty()) return angle::Result::Continue; + mLayerFetchImageView.resize(gl::kCubeFaceCount); mCubeMapRenderTargets.resize(gl::kCubeFaceCount); for (size_t cubeMapFaceIndex = 0; cubeMapFaceIndex < gl::kCubeFaceCount; ++cubeMapFaceIndex) { - vk::ImageView *imageView; - ANGLE_TRY(getLayerLevelDrawImageView(contextVk, cubeMapFaceIndex, 0, &imageView)); - mCubeMapRenderTargets[cubeMapFaceIndex].init(mImage, imageView, getNativeImageLevel(0), - getNativeImageLayer(cubeMapFaceIndex)); + vk::ImageView *drawView; + ANGLE_TRY(getLayerLevelDrawImageView(contextVk, cubeMapFaceIndex, 0, &drawView)); + + // Users of the render target expect the views to directly view the desired layer, so we + // need create a fetch view for each layer as well. + gl::SwizzleState mappedSwizzle; + MapSwizzleState(mImage->getFormat(), mState.getSwizzleState(), &mappedSwizzle); + gl::TextureType arrayType = Get2DTextureType(gl::kCubeFaceCount, mImage->getSamples()); + ANGLE_TRY(mImage->initLayerImageView(contextVk, arrayType, mImage->getAspectFlags(), + mappedSwizzle, &mLayerFetchImageView[cubeMapFaceIndex], + getNativeImageLevel(0), 1, + getNativeImageLayer(cubeMapFaceIndex), 1)); + + mCubeMapRenderTargets[cubeMapFaceIndex].init( + mImage, drawView, &mLayerFetchImageView[cubeMapFaceIndex], getNativeImageLevel(0), + getNativeImageLayer(cubeMapFaceIndex)); } return angle::Result::Continue; } @@ -1302,8 +1281,7 @@ { ASSERT(mImage->valid()); - const GLenum minFilter = mState.getSamplerState().getMinFilter(); - if (minFilter == GL_LINEAR || minFilter == GL_NEAREST) + if (!gl::IsMipmapFiltered(mState.getSamplerState())) { return mReadBaseLevelImageView; } @@ -1311,6 +1289,23 @@ return mReadMipmapImageView; } +const vk::ImageView &TextureVk::getFetchImageView() const +{ + if (!mFetchBaseLevelImageView.valid()) + { + return getReadImageView(); + } + + ASSERT(mImage->valid()); + + if (!gl::IsMipmapFiltered(mState.getSamplerState())) + { + return mFetchBaseLevelImageView; + } + + return mFetchMipmapImageView; +} + angle::Result TextureVk::getLayerLevelDrawImageView(vk::Context *context, size_t layer, size_t level, @@ -1428,6 +1423,17 @@ ANGLE_TRY(mImage->initLayerImageView(contextVk, mState.getType(), aspectFlags, mappedSwizzle, &mReadBaseLevelImageView, baseLevel, 1, baseLayer, layerCount)); + if (mState.getType() == gl::TextureType::CubeMap) + { + gl::TextureType arrayType = Get2DTextureType(layerCount, mImage->getSamples()); + + ANGLE_TRY(mImage->initLayerImageView(contextVk, arrayType, aspectFlags, mappedSwizzle, + &mFetchMipmapImageView, baseLevel, levelCount, + baseLayer, layerCount)); + ANGLE_TRY(mImage->initLayerImageView(contextVk, arrayType, aspectFlags, mappedSwizzle, + &mFetchBaseLevelImageView, baseLevel, 1, baseLayer, + layerCount)); + } if (!format.imageFormat().isBlock) { ANGLE_TRY(mImage->initLayerImageView(contextVk, mState.getType(), aspectFlags, @@ -1457,18 +1463,22 @@ renderer->releaseObject(currentSerial, &mDrawBaseLevelImageView); renderer->releaseObject(currentSerial, &mReadBaseLevelImageView); renderer->releaseObject(currentSerial, &mReadMipmapImageView); + renderer->releaseObject(currentSerial, &mFetchBaseLevelImageView); + renderer->releaseObject(currentSerial, &mFetchMipmapImageView); for (auto &layerViews : mLayerLevelDrawImageViews) { for (vk::ImageView &imageView : layerViews) { - if (imageView.valid()) - { - renderer->releaseObject(currentSerial, &imageView); - } + renderer->releaseObject(currentSerial, &imageView); } } mLayerLevelDrawImageViews.clear(); + for (vk::ImageView &imageView : mLayerFetchImageView) + { + renderer->releaseObject(currentSerial, &imageView); + } + mLayerFetchImageView.clear(); mCubeMapRenderTargets.clear(); }
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.h b/src/libANGLE/renderer/vulkan/TextureVk.h index 21b6192..178ce23 100644 --- a/src/libANGLE/renderer/vulkan/TextureVk.h +++ b/src/libANGLE/renderer/vulkan/TextureVk.h
@@ -153,6 +153,8 @@ void releaseOwnershipOfImage(const gl::Context *context); const vk::ImageView &getReadImageView() const; + // A special view for cube maps as a 2D array, used with shaders that do texelFetch(). + const vk::ImageView &getFetchImageView() const; angle::Result getLayerLevelDrawImageView(vk::Context *context, size_t layer, size_t level, @@ -249,7 +251,6 @@ const gl::Offset &destOffset, const vk::Format &destFormat, size_t sourceLevel, - size_t sourceLayer, const gl::Rectangle &sourceArea, bool isSrcFlipY, bool unpackFlipY, @@ -293,10 +294,13 @@ vk::ImageView mDrawBaseLevelImageView; vk::ImageView mReadBaseLevelImageView; vk::ImageView mReadMipmapImageView; + vk::ImageView mFetchBaseLevelImageView; + vk::ImageView mFetchMipmapImageView; std::vector<std::vector<vk::ImageView>> mLayerLevelDrawImageViews; vk::Sampler mSampler; RenderTargetVk mRenderTarget; + std::vector<vk::ImageView> mLayerFetchImageView; std::vector<RenderTargetVk> mCubeMapRenderTargets; };