1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // RenderbufferVk.cpp:
7 // Implements the class methods for RenderbufferVk.
8 //
9
10 #include "libANGLE/renderer/vulkan/RenderbufferVk.h"
11
12 #include "libANGLE/Context.h"
13 #include "libANGLE/Image.h"
14 #include "libANGLE/renderer/vulkan/ContextVk.h"
15 #include "libANGLE/renderer/vulkan/ImageVk.h"
16 #include "libANGLE/renderer/vulkan/TextureVk.h"
17 #include "libANGLE/renderer/vulkan/vk_renderer.h"
18
19 namespace rx
20 {
21 namespace
22 {
23 angle::SubjectIndex kRenderbufferImageSubjectIndex = 0;
24 } // namespace
25
RenderbufferVk(const gl::RenderbufferState & state)26 RenderbufferVk::RenderbufferVk(const gl::RenderbufferState &state)
27 : RenderbufferImpl(state),
28 mOwnsImage(false),
29 mImage(nullptr),
30 mImageObserverBinding(this, kRenderbufferImageSubjectIndex)
31 {}
32
~RenderbufferVk()33 RenderbufferVk::~RenderbufferVk() {}
34
onDestroy(const gl::Context * context)35 void RenderbufferVk::onDestroy(const gl::Context *context)
36 {
37 ContextVk *contextVk = vk::GetImpl(context);
38 releaseAndDeleteImage(contextVk);
39 }
40
setStorageImpl(const gl::Context * context,GLsizei samples,GLenum internalformat,GLsizei width,GLsizei height,gl::MultisamplingMode mode)41 angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context,
42 GLsizei samples,
43 GLenum internalformat,
44 GLsizei width,
45 GLsizei height,
46 gl::MultisamplingMode mode)
47 {
48 ContextVk *contextVk = vk::GetImpl(context);
49 vk::Renderer *renderer = contextVk->getRenderer();
50 const vk::Format &format = renderer->getFormat(internalformat);
51 angle::FormatID textureFormatID = format.getActualRenderableImageFormatID();
52
53 if (!mOwnsImage)
54 {
55 releaseAndDeleteImage(contextVk);
56 }
57
58 if (mImage != nullptr && mImage->valid())
59 {
60 // Check against the state if we need to recreate the storage.
61 if (internalformat != mState.getFormat().info->internalFormat ||
62 width != mState.getWidth() || height != mState.getHeight() ||
63 samples != mState.getSamples() || mode != mState.getMultisamplingMode())
64 {
65 releaseImage(contextVk);
66 }
67 }
68
69 if ((mImage != nullptr && mImage->valid()) || width == 0 || height == 0)
70 {
71 return angle::Result::Continue;
72 }
73
74 if (mImage == nullptr)
75 {
76 mImage = new vk::ImageHelper();
77 mOwnsImage = true;
78 mImageSiblingSerial = {};
79 mImageObserverBinding.bind(mImage);
80 mImageViews.init(renderer);
81 }
82
83 const angle::Format &textureFormat = format.getActualRenderableImageFormat();
84 const bool isDepthStencilFormat = textureFormat.hasDepthOrStencilBits();
85 ASSERT(textureFormat.redBits > 0 || isDepthStencilFormat);
86
87 const bool isRenderToTexture = mode == gl::MultisamplingMode::MultisampledRenderToTexture;
88 const bool hasRenderToTextureEXT =
89 renderer->getFeatures().supportsMultisampledRenderToSingleSampled.enabled;
90
91 // Transfer and sampled usage are used for various utilities such as readback or blit.
92 VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
93 VK_IMAGE_USAGE_SAMPLED_BIT;
94
95 // Renderbuffer's normal usage is as framebuffer attachment.
96 usage |= isDepthStencilFormat ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT
97 : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
98
99 // When used to emulate multisampled render to texture, it can be read as input attachment.
100 if (isRenderToTexture && !hasRenderToTextureEXT)
101 {
102 usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
103 }
104
105 // For framebuffer fetch and advanced blend emulation, color will be read as input attachment.
106 if (!isDepthStencilFormat)
107 {
108 usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
109 }
110
111 VkImageCreateFlags createFlags = vk::kVkImageCreateFlagsNone;
112 if (isRenderToTexture &&
113 renderer->getFeatures().supportsMultisampledRenderToSingleSampled.enabled)
114 {
115 createFlags |= VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT;
116 }
117
118 if (contextVk->getFeatures().limitSampleCountTo2.enabled)
119 {
120 samples = std::min(samples, 2);
121 }
122
123 const uint32_t imageSamples = isRenderToTexture ? 1 : samples;
124
125 bool robustInit = contextVk->isRobustResourceInitEnabled();
126
127 VkExtent3D extents = {static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1u};
128 ANGLE_TRY(mImage->initExternal(
129 contextVk, gl::TextureType::_2D, extents, format.getIntendedFormatID(), textureFormatID,
130 imageSamples, usage, createFlags, vk::ImageLayout::Undefined, nullptr, gl::LevelIndex(0), 1,
131 1, robustInit, false, vk::YcbcrConversionDesc{}));
132
133 VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
134 ANGLE_TRY(contextVk->initImageAllocation(mImage, false, renderer->getMemoryProperties(), flags,
135 vk::MemoryAllocationType::RenderBufferStorageImage));
136
137 // If multisampled render to texture, an implicit multisampled image is created which is used as
138 // the color or depth/stencil attachment. At the end of the render pass, this image is
139 // automatically resolved into |mImage| and its contents are discarded.
140 if (isRenderToTexture && !hasRenderToTextureEXT)
141 {
142 mMultisampledImageViews.init(renderer);
143
144 ANGLE_TRY(mMultisampledImage.initImplicitMultisampledRenderToTexture(
145 contextVk, false, renderer->getMemoryProperties(), gl::TextureType::_2D, samples,
146 *mImage, mImage->getExtents(), robustInit));
147
148 mRenderTarget.init(&mMultisampledImage, &mMultisampledImageViews, mImage, &mImageViews,
149 mImageSiblingSerial, gl::LevelIndex(0), 0, 1,
150 RenderTargetTransience::MultisampledTransient);
151 }
152 else
153 {
154 mRenderTarget.init(mImage, &mImageViews, nullptr, nullptr, mImageSiblingSerial,
155 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
156 }
157
158 return angle::Result::Continue;
159 }
160
setStorage(const gl::Context * context,GLenum internalformat,GLsizei width,GLsizei height)161 angle::Result RenderbufferVk::setStorage(const gl::Context *context,
162 GLenum internalformat,
163 GLsizei width,
164 GLsizei height)
165 {
166 // The ES 3.0 spec(section 4.4.2.1) states that RenderbufferStorage is equivalent to calling
167 // RenderbufferStorageMultisample with samples equal to zero.
168 return setStorageImpl(context, 0, internalformat, width, height,
169 gl::MultisamplingMode::Regular);
170 }
171
setStorageMultisample(const gl::Context * context,GLsizei samples,GLenum internalformat,GLsizei width,GLsizei height,gl::MultisamplingMode mode)172 angle::Result RenderbufferVk::setStorageMultisample(const gl::Context *context,
173 GLsizei samples,
174 GLenum internalformat,
175 GLsizei width,
176 GLsizei height,
177 gl::MultisamplingMode mode)
178 {
179 return setStorageImpl(context, samples, internalformat, width, height, mode);
180 }
181
setStorageEGLImageTarget(const gl::Context * context,egl::Image * image)182 angle::Result RenderbufferVk::setStorageEGLImageTarget(const gl::Context *context,
183 egl::Image *image)
184 {
185 ContextVk *contextVk = vk::GetImpl(context);
186 vk::Renderer *renderer = contextVk->getRenderer();
187
188 ANGLE_TRY(contextVk->getShareGroup()->lockDefaultContextsPriority(contextVk));
189
190 releaseAndDeleteImage(contextVk);
191
192 ImageVk *imageVk = vk::GetImpl(image);
193 mImage = imageVk->getImage();
194 mOwnsImage = false;
195 mImageSiblingSerial = imageVk->generateSiblingSerial();
196 mImageObserverBinding.bind(mImage);
197 mImageViews.init(renderer);
198
199 const vk::Format &vkFormat = renderer->getFormat(image->getFormat().info->sizedInternalFormat);
200 const angle::Format &textureFormat = vkFormat.getActualRenderableImageFormat();
201
202 VkImageAspectFlags aspect = vk::GetFormatAspectFlags(textureFormat);
203
204 // Transfer the image to this queue if needed
205 if (mImage->isQueueFamilyChangeNeccesary(contextVk->getDeviceQueueIndex()))
206 {
207 vk::OutsideRenderPassCommandBuffer *commandBuffer;
208 vk::CommandBufferAccess access;
209 access.onExternalAcquireRelease(mImage);
210 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
211 mImage->changeLayoutAndQueue(contextVk, aspect, vk::ImageLayout::ColorWrite,
212 contextVk->getDeviceQueueIndex(), commandBuffer);
213
214 ANGLE_TRY(contextVk->onEGLImageQueueChange());
215 }
216
217 mRenderTarget.init(mImage, &mImageViews, nullptr, nullptr, mImageSiblingSerial,
218 imageVk->getImageLevel(), imageVk->getImageLayer(), 1,
219 RenderTargetTransience::Default);
220
221 return angle::Result::Continue;
222 }
223
copyRenderbufferSubData(const gl::Context * context,const gl::Renderbuffer * srcBuffer,GLint srcLevel,GLint srcX,GLint srcY,GLint srcZ,GLint dstLevel,GLint dstX,GLint dstY,GLint dstZ,GLsizei srcWidth,GLsizei srcHeight,GLsizei srcDepth)224 angle::Result RenderbufferVk::copyRenderbufferSubData(const gl::Context *context,
225 const gl::Renderbuffer *srcBuffer,
226 GLint srcLevel,
227 GLint srcX,
228 GLint srcY,
229 GLint srcZ,
230 GLint dstLevel,
231 GLint dstX,
232 GLint dstY,
233 GLint dstZ,
234 GLsizei srcWidth,
235 GLsizei srcHeight,
236 GLsizei srcDepth)
237 {
238 RenderbufferVk *sourceVk = vk::GetImpl(srcBuffer);
239
240 // Make sure the source/destination targets are initialized and all staged updates are flushed.
241 ANGLE_TRY(sourceVk->ensureImageInitialized(context));
242 ANGLE_TRY(ensureImageInitialized(context));
243
244 return vk::ImageHelper::CopyImageSubData(context, sourceVk->getImage(), srcLevel, srcX, srcY,
245 srcZ, mImage, dstLevel, dstX, dstY, dstZ, srcWidth,
246 srcHeight, srcDepth);
247 }
248
copyTextureSubData(const gl::Context * context,const gl::Texture * srcTexture,GLint srcLevel,GLint srcX,GLint srcY,GLint srcZ,GLint dstLevel,GLint dstX,GLint dstY,GLint dstZ,GLsizei srcWidth,GLsizei srcHeight,GLsizei srcDepth)249 angle::Result RenderbufferVk::copyTextureSubData(const gl::Context *context,
250 const gl::Texture *srcTexture,
251 GLint srcLevel,
252 GLint srcX,
253 GLint srcY,
254 GLint srcZ,
255 GLint dstLevel,
256 GLint dstX,
257 GLint dstY,
258 GLint dstZ,
259 GLsizei srcWidth,
260 GLsizei srcHeight,
261 GLsizei srcDepth)
262 {
263 ContextVk *contextVk = vk::GetImpl(context);
264 TextureVk *sourceVk = vk::GetImpl(srcTexture);
265
266 // Make sure the source/destination targets are initialized and all staged updates are flushed.
267 ANGLE_TRY(sourceVk->ensureImageInitialized(contextVk, ImageMipLevels::EnabledLevels));
268 ANGLE_TRY(ensureImageInitialized(context));
269
270 return vk::ImageHelper::CopyImageSubData(context, &sourceVk->getImage(), srcLevel, srcX, srcY,
271 srcZ, mImage, dstLevel, dstX, dstY, dstZ, srcWidth,
272 srcHeight, srcDepth);
273 }
274
getAttachmentRenderTarget(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex,GLsizei samples,FramebufferAttachmentRenderTarget ** rtOut)275 angle::Result RenderbufferVk::getAttachmentRenderTarget(const gl::Context *context,
276 GLenum binding,
277 const gl::ImageIndex &imageIndex,
278 GLsizei samples,
279 FramebufferAttachmentRenderTarget **rtOut)
280 {
281 ASSERT(mImage && mImage->valid());
282 *rtOut = &mRenderTarget;
283 return angle::Result::Continue;
284 }
285
initializeContents(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex)286 angle::Result RenderbufferVk::initializeContents(const gl::Context *context,
287 GLenum binding,
288 const gl::ImageIndex &imageIndex)
289 {
290 // Note: stageSubresourceRobustClear only uses the intended format to count channels.
291 mImage->stageRobustResourceClear(imageIndex);
292 return mImage->flushAllStagedUpdates(vk::GetImpl(context));
293 }
294
releaseOwnershipOfImage(const gl::Context * context)295 void RenderbufferVk::releaseOwnershipOfImage(const gl::Context *context)
296 {
297 ContextVk *contextVk = vk::GetImpl(context);
298
299 ASSERT(!mImageSiblingSerial.valid());
300
301 mOwnsImage = false;
302 releaseAndDeleteImage(contextVk);
303 }
304
releaseAndDeleteImage(ContextVk * contextVk)305 void RenderbufferVk::releaseAndDeleteImage(ContextVk *contextVk)
306 {
307 releaseImage(contextVk);
308 SafeDelete(mImage);
309 mImageObserverBinding.bind(nullptr);
310 }
311
releaseImage(ContextVk * contextVk)312 void RenderbufferVk::releaseImage(ContextVk *contextVk)
313 {
314 vk::Renderer *renderer = contextVk->getRenderer();
315 if (mImage == nullptr)
316 {
317 ASSERT(mImageViews.isImageViewGarbageEmpty() &&
318 mMultisampledImageViews.isImageViewGarbageEmpty());
319 }
320 else
321 {
322 mRenderTarget.release(contextVk);
323 mImageViews.release(renderer, mImage->getResourceUse());
324 mMultisampledImageViews.release(renderer, mImage->getResourceUse());
325 }
326
327 if (mImage && mOwnsImage)
328 {
329 mImage->releaseImageFromShareContexts(renderer, contextVk, mImageSiblingSerial);
330 mImage->releaseStagedUpdates(renderer);
331 }
332 else
333 {
334 if (mImage)
335 {
336 mImage->finalizeImageLayoutInShareContexts(renderer, contextVk, mImageSiblingSerial);
337 }
338 mImage = nullptr;
339 mImageObserverBinding.bind(nullptr);
340 }
341
342 if (mMultisampledImage.valid())
343 {
344 mMultisampledImage.releaseImageFromShareContexts(renderer, contextVk, mImageSiblingSerial);
345 }
346 }
347
getImplementationSizedFormat() const348 const gl::InternalFormat &RenderbufferVk::getImplementationSizedFormat() const
349 {
350 GLenum internalFormat = mImage->getActualFormat().glInternalFormat;
351 return gl::GetSizedInternalFormatInfo(internalFormat);
352 }
353
getColorReadFormat(const gl::Context * context)354 GLenum RenderbufferVk::getColorReadFormat(const gl::Context *context)
355 {
356 const gl::InternalFormat &sizedFormat = getImplementationSizedFormat();
357 return sizedFormat.format;
358 }
359
getColorReadType(const gl::Context * context)360 GLenum RenderbufferVk::getColorReadType(const gl::Context *context)
361 {
362 const gl::InternalFormat &sizedFormat = getImplementationSizedFormat();
363 return sizedFormat.type;
364 }
365
getRenderbufferImage(const gl::Context * context,const gl::PixelPackState & packState,gl::Buffer * packBuffer,GLenum format,GLenum type,void * pixels)366 angle::Result RenderbufferVk::getRenderbufferImage(const gl::Context *context,
367 const gl::PixelPackState &packState,
368 gl::Buffer *packBuffer,
369 GLenum format,
370 GLenum type,
371 void *pixels)
372 {
373 // Storage not defined.
374 if (!mImage || !mImage->valid())
375 {
376 return angle::Result::Continue;
377 }
378
379 ContextVk *contextVk = vk::GetImpl(context);
380 ANGLE_TRY(mImage->flushAllStagedUpdates(contextVk));
381
382 gl::MaybeOverrideLuminance(format, type, getColorReadFormat(context),
383 getColorReadType(context));
384
385 return mImage->readPixelsForGetImage(contextVk, packState, packBuffer, gl::LevelIndex(0), 0, 0,
386 format, type, pixels);
387 }
388
ensureImageInitialized(const gl::Context * context)389 angle::Result RenderbufferVk::ensureImageInitialized(const gl::Context *context)
390 {
391 ANGLE_TRY(setStorage(context, mState.getFormat().info->internalFormat, mState.getWidth(),
392 mState.getHeight()));
393
394 return mImage->flushAllStagedUpdates(vk::GetImpl(context));
395 }
396
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)397 void RenderbufferVk::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
398 {
399 ASSERT(index == kRenderbufferImageSubjectIndex &&
400 (message == angle::SubjectMessage::SubjectChanged ||
401 message == angle::SubjectMessage::InitializationComplete));
402
403 // Forward the notification to the parent class that the staging buffer changed.
404 if (message == angle::SubjectMessage::SubjectChanged)
405 {
406 onStateChange(angle::SubjectMessage::SubjectChanged);
407 }
408 }
409 } // namespace rx
410