Overte C++ Documentation
VKTexture.h
1 //
2 // Created by Bradley Austin Davis on 2016/08/07
3 // Adapted for Vulkan in 2022-2025 by dr Karol Suprynowicz.
4 // Copyright 2013-2018 High Fidelity, Inc.
5 // Copyright 2023-2025 Overte e.V.
6 //
7 // Distributed under the Apache License, Version 2.0.
8 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9 // SPDX-License-Identifier: Apache-2.0
10 //
11 // Contains parts of Vulkan Samples, Copyright (c) 2018, Sascha Willems, distributed on MIT License.
12 //
13 
14 #ifndef hifi_gpu_vk_VKTexture_h
15 #define hifi_gpu_vk_VKTexture_h
16 
17 #include "VKShared.h"
18 
19 #include "VKBackend.h"
20 #include "VKTextureTransfer.h"
21 
22 namespace gpu { namespace vk {
23 
24 struct VKFilterMode {
25  VkFilter minFilter;
26  VkFilter magFilter;
27 };
28 
29 class VKTexture : public VKObject<Texture> {
30 public:
31  // VKTODO
32  //static void initTextureTransferHelper();
33  //static std::shared_ptr<VKTextureTransferHelper> _textureTransferHelper;
34 
35  /*template <typename VKTextureType>
36  static VKTextureType* sync(VKBackend& backend, const TexturePointer& texturePointer, bool needTransfer) {
37  const Texture& texture = *texturePointer;
38  if (!texture.isDefined()) {
39  // NO texture definition yet so let's avoid thinking
40  return nullptr;
41  }
42 
43  // If the object hasn't been created, or the object definition is out of date, drop and re-create
44  VKTextureType* object = Backend::getGPUObject<VKTextureType>(texture);
45 
46  // Create the texture if need be (force re-creation if the storage stamp changes
47  // for easier use of immutable storage)
48  if (!object || object->isInvalid()) {
49  // This automatically any previous texture
50  object = new VKTextureType(backend.shared_from_this(), texture, needTransfer);
51  if (!object->_transferable) {
52  object->createTexture(backend);
53  object->_contentStamp = texture.getDataStamp();
54  object->postTransfer(backend);
55  }
56  }
57 
58  // Object maybe doesn't need to be transferred after creation
59  if (!object->_transferable) {
60  return object;
61  }
62 
63  // If we just did a transfer, return the object after doing post-transfer work
64  if (VKSyncState::Transferred == object->getSyncState()) {
65  object->postTransfer(backend);
66  return object;
67  }
68 
69  if (object->isReady()) {
70  // Do we need to reduce texture memory usage?
71  if (object->isOverMaxMemory() && texturePointer->incremementMinMip()) {
72  // WARNING, this code path will essentially `delete this`,
73  // so no dereferencing of this instance should be done past this point
74  object = new VKTextureType(backend.shared_from_this(), texture, object);
75  // VKTODO
76  // _textureTransferHelper->transferTexture(texturePointer);
77  }
78  } else if (object->isOutdated()) {
79  // Object might be outdated, if so, start the transfer
80  // (outdated objects that are already in transfer will have reported 'true' for ready()
81  // VKTODO
82  // _textureTransferHelper->transferTexture(texturePointer);
83  }
84 
85  return object;
86  }*/
87 
88  /*template <typename VKTextureType>
89  static VkDescriptorImageInfo getDescriptorImageInfoFromTexture(VKBackend& backend, const TexturePointer& texture, bool shouldSync) {
90  if (!texture) {
91  Q_ASSERT(false);
92  return backend.getDefaultTextureDescriptorInfo();
93  }
94  VKTextureType* object { nullptr };
95  if (shouldSync) {
96  object = sync<VKTextureType>(backend, texture, shouldSync);
97  } else {
98  object = Backend::getGPUObject<VKTextureType>(*texture);
99  }
100  if (!object) {
101  Q_ASSERT(false);
102  return backend.getDefaultTextureDescriptorInfo();
103  }
104 
105  VkDescriptorImageInfo result {};
106  //= object->_;
107 
108  // VKTODO
109  // Don't return textures that are in transfer state
110  //if (shouldSync) {
111  // if ((object->getSyncState() != VKSyncState::Idle) ||
112  // // Don't return transferable textures that have never completed transfer
113  // (!object->_transferable || 0 != object->_transferCount)) {
114  // // Will be either 0 or the original texture being downsampled.
115  // Q_ASSERT(false);
116  // result = object->_downsampleSource._texture;
117  // }
118  //}
119 
120  return object->getDescriptorImageInfo();
121  }*/
122 
123  virtual ~VKTexture();
124 
125  VkImage _vkImage{ VK_NULL_HANDLE };
126  const Stamp _storageStamp;
127  const VkImageViewType _target;
128  //const uint16 _maxMip;
129  //const uint16 _minMip;
130  //const Size _virtualSize; // theoretical size as expected
131  Stamp _contentStamp { 0 };
132  const bool _transferable;
133  Size _transferCount { 0 };
134 
135  struct DownsampleSource {
136  using Pointer = std::shared_ptr<DownsampleSource>;
137  DownsampleSource(const std::weak_ptr<VKBackend>& backend) : _backend(backend), _size(0), _texture(VK_NULL_HANDLE), _minMip(0), _maxMip(0) {}
138  DownsampleSource(const std::weak_ptr<VKBackend>& backend, VKTexture* originalTexture) : _backend(backend), _size(0), _texture(0), _minMip(0), _maxMip(0) { Q_ASSERT(false); }; // VKTODO
139  ~DownsampleSource() {}; // VKTODO
140  void reset() { _texture = VK_NULL_HANDLE; }
141  const std::weak_ptr<vk::VKBackend>& _backend;
142  const Size _size { 0 };
143  // TODO: Shouldn't that be texture instead?
144  VkImage _texture { VK_NULL_HANDLE };
145  const uint16 _minMip { 0 };
146  const uint16 _maxMip { 0 };
147  } _downsampleSource;
148 
149  virtual size_t size() const { Q_ASSERT(false); return 0; }
150  VKSyncState getSyncState() const { return _syncState; }
151 
152  // Is the storage out of date relative to the gpu texture?
153  // VKTODO
154  bool isInvalid() const { return false; };
155 
156  // Is the content out of date relative to the gpu texture?
157  // VKTODO
158  bool isOutdated() const { return false; };
159 
160  // Is the texture in a state where it can be rendered with no work?
161  bool isReady() const {
162  // VKTODO: Is this right?
163  return _syncState == VKSyncState::Transferred;
164  };
165 
166  // Execute any post-move operations that must occur only on the main thread
167  virtual void postTransfer(VKBackend &backend) = 0;
168 
169  // VKTODO: can be done later
170  bool isOverMaxMemory() const { return false; };
171 
172  static uint8_t getFaceCount(VkImageViewType target);
173 
174 protected:
175  static const size_t CUBE_NUM_FACES = 6;
176  // VKTODO
177  //static const uint8_t TEXTURE_CUBE_NUM_FACES = 6;
178  //static const uint32_t CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES];
179  static const VKFilterMode FILTER_MODES[Sampler::NUM_FILTERS];
180  static const uint32_t WRAP_MODES[Sampler::NUM_WRAP_MODES];
181 
182  //static const std::vector<VKenum>& getFaceTargets(VKenum textureType);
183 
184  static VkImageViewType getVKTextureType(const Texture& texture);
185  // Return a floating point value indicating how much of the allowed
186  // texture memory we are currently consuming. A value of 0 indicates
187  // no texture memory usage, while a value of 1 indicates all available / allowed memory
188  // is consumed. A value above 1 indicates that there is a problem.
189  // VKTODO: this can wait for now
190  static float getMemoryPressure() { return 0; };
191 
192 
193  //const size_t _size { 0 }; // true size as reported by the Vulkan api
194  std::atomic<VKSyncState> _syncState { VKSyncState::Idle };
195  VkFormat _vkTexelFormat { VK_FORMAT_UNDEFINED };
196 
197  VKTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture, bool isTransferable);
198  //VKTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture, VkImage image, bool transferrable);
199  //VKTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture, VkImage image, VKTexture* originalTexture);
200 
201  void setSyncState(VKSyncState syncState) { _syncState = syncState; }
202  //uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; }
203 
204  virtual void createTexture(VKBackend &backend) = 0;
205  virtual VkDescriptorImageInfo getDescriptorImageInfo() = 0;
206 
207  //virtual void allocateStorage() const = 0;
208  //virtual void updateSize() const = 0;
209  struct TransferData {
210  uint16_t mipLevels;
211  uint16_t width;
212  uint16_t height;
213  size_t buffer_size;
214  struct Mip {
215  size_t offset;
216  size_t size;
217  uint32_t width;
218  uint32_t height;
219  bool needsAddingAlpha;
220  bool needsBGRToRGB;
221  std::shared_ptr<const storage::Storage> data;
222  };
223  std::vector<std::vector<Mip>> mips;
224  };
225  virtual void transfer(VKBackend &backend) = 0;
226  virtual void syncSampler(const Sampler& sampler) = 0;
227  // VKTODO
228  //virtual void generateMips() const = 0;
229  // VKTODO what does this mean?
230  //virtual void withPreservedTexture(std::function<void()> f) const = 0;
231 
232  static Sampler getInvalidSampler();
233 
234  // This stores the texture handle (64 bits) in xy, the min mip available in z, and the sampler ID in w
235  mutable Sampler _cachedSampler { getInvalidSampler() };
236 
237 protected:
238  void setSize(size_t size) const;
239 
240 private:
241 
242  VKTexture(const std::weak_ptr<VKBackend>& backend, const gpu::Texture& gpuTexture, VkImage image, VKTexture* originalTexture, bool transferrable);
243 
244  friend class VKTextureTransferHelper;
245  friend class VKBackend;
246 };
247 
248 class VKFixedAllocationTexture : public VKTexture {
249  using Parent = VKTexture;
250  friend class VKBackend;
251 
252 public:
253  VKFixedAllocationTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture, bool isTransferable) :
254  VKTexture(backend, texture, isTransferable) {}; // VKTODO
255  virtual ~VKFixedAllocationTexture() {};
256 
257 protected:
258  Size size() const override { Q_ASSERT(false); return 0; }
259 
260  //void allocateStorage();
261  // VKTODO
262  void syncSampler(const Sampler& sampler) override {};
263  // VKTODO
264  //void updateSize() const override {};
265  VmaAllocation _vmaAllocation;
266  //const Size _size{ 0 };
267 };
268 
269 class VKAttachmentTexture : public VKFixedAllocationTexture {
270  friend class VKBackend;
271  friend class VKFramebuffer;
272 
273 public:
274  VkImageLayout getVkImageLayout() { return _vkImageLayout; };
275 
276 protected:
277  VKAttachmentTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture) :
278  VKFixedAllocationTexture(backend, texture, false) {
279  VKAttachmentTexture::createTexture(*backend.lock());
280  };
281  virtual ~VKAttachmentTexture(); // VKTODO: delete image and image view, release memory
282  void createTexture(VKBackend &backend) override;
283  void transfer(VKBackend &backend) override {}; // VKTODO
284  void postTransfer(VKBackend &backend) override {}; // VKTODO
285 
286  VkDescriptorImageInfo getDescriptorImageInfo() override; // VKTODO
287 
288  VkImageView _vkImageView { VK_NULL_HANDLE }; // Created by VKFramebuffer class.
289  VkImageLayout _vkImageLayout {}; // VKTODO: this needs to be updated on blits and other image writes
290  VkSampler _vkSampler { VK_NULL_HANDLE };
291 };
292 
293 class VKStrictResourceTexture: public VKFixedAllocationTexture {
294  friend class VKBackend;
295 public:
296  void postTransfer(VKBackend &backend) override;
297 
298 protected:
299  // VKTODO: how to handle mipmaps?
300  VKStrictResourceTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture) :
301  VKFixedAllocationTexture(backend, texture, true) {
302  VKBackend& vkBackend = *backend.lock();
303  VKStrictResourceTexture::createTexture(vkBackend);
304  // VKTODO: transfer on transfer tread
305  VKStrictResourceTexture::transfer(vkBackend);
306  VKStrictResourceTexture::postTransfer(vkBackend);
307  };
308  ~VKStrictResourceTexture() override; // VKTODO: delete image and image view, release memory
309  void createTexture(VKBackend &backend) override;
310  void transfer(VKBackend &backend) override;
311  VkDescriptorImageInfo getDescriptorImageInfo() override;
312  TransferData _transferData{};
313  //VkImage _vkImage { VK_NULL_HANDLE };
314  VkImageView _vkImageView { VK_NULL_HANDLE };
315  VkImageLayout _vkImageLayout {};
316  VkSampler _vkSampler { VK_NULL_HANDLE };
317  // This need to be moved to VKFixedAllocationTexture and allocated in allocateStorage()
318  //VkDeviceMemory _vkDeviceMemory{ VK_NULL_HANDLE };
319 };
320 
321 class VKVariableAllocationTexture : public VKTexture, public VKVariableAllocationSupport {
322  using Parent = VKTexture;
323  friend class VKBackend;
324  using PromoteLambda = std::function<void()>;
325 
326 protected:
327  VKVariableAllocationTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture);
328  ~VKVariableAllocationTexture() override;
329 
330  void allocateNewImage(uint16_t targetAllocatedMip, VkCommandBuffer &copyCmd);
331 
332  Size size() const override { return _size; }
333 
334  void syncSampler(const Sampler& sampler) override {};
335 
336  void copyTextureMipsInGPUMem(VkImage srcImage, VkImage dstImage,
337  uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips, VkCommandBuffer &copyCmd);
338 
339  VmaAllocation _vmaAllocation;
340 
341 #if GPU_BINDLESS_TEXTURES
342  virtual const Bindless& getBindless() const override;
343 #endif
344 };
345 
346 class VKResourceTexture : public VKVariableAllocationTexture {
347  using Parent = VKVariableAllocationTexture;
348  friend class VKBackend;
349 public:
350  size_t promote() override;
351  size_t demote() override;
352  void populateTransferQueue(TransferQueue& pendingTransfers) override;
353 
354  void postTransfer(VKBackend &backend) override;
355 
356 protected:
357  VKResourceTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture);
358 
359  void syncSampler(const Sampler& sampler) override;
360  void updateImageView();
361 
362  void createTexture(VKBackend &backend) override {}; // VKTODO
363  void transfer(VKBackend &backend) override {}; // VKTODO
364 
365  VkDescriptorImageInfo getDescriptorImageInfo() override;
366 
367  //void allocateStorage(uint16 mip);
368  Size copyMipsFromTexture(VkCommandBuffer &copyCmd);
369  Size copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, VkCommandBuffer &copyCmd);
370  Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face,
371  const uvec3& size, uint32_t yOffset,
372  Size sourceSize, const void* sourcePointer);
373 
374  VkImageView _vkImageView { VK_NULL_HANDLE };
375  VkImageLayout _vkImageLayout {};
376  VkSampler _vkSampler { VK_NULL_HANDLE };
377  friend class TransferJob;
378 };
379 
380 
381 class VKExternalTexture: public VKTexture {
382  friend class VKBackend;
383 
384 public:
385  VKExternalTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture) :
386  VKTexture(backend, texture, false) {
387  VKBackend& vkBackend = *backend.lock();
388  VKExternalTexture::createTexture(vkBackend);
389  // VKTODO: maybe this needs to be done on OpenGL thread?
390  VKExternalTexture::initGL(vkBackend);
391  //VKExternalTexture::transferGL(vkBackend);
392  VKExternalTexture::postTransfer(vkBackend);
393  }; // VKTODO
394  ~VKExternalTexture() override; // VKTODO: add proper cleanup for both Vulkan and OpenGL
395  void releaseExternalTexture(); // Called on destructor and when new texture is set
396  void setSource(GLuint source);
397  void transferGL(VKBackend &backend);
398 
399 protected:
400  Size size() const override { return _size; }
401 
402  void syncSampler(const Sampler& sampler) override {};
403 
404  //VmaAllocation _vmaAllocation;
405  VkDeviceMemory _sharedMemory;
406  size_t _sharedMemorySize;
407  const Size _size{ 0 }; // VKTODO: how is this used?
408 
409  void createTexture(VKBackend &backend) override;
410  void initGL(VKBackend &backend);
411  void transfer(VKBackend &backend) override {};
412  void postTransfer(VKBackend &backend) override;
413  VkDescriptorImageInfo getDescriptorImageInfo() override;
414  VkImageView _vkImageView { VK_NULL_HANDLE };
415  VkImageLayout _vkImageLayout {};
416  VkSampler _vkSampler { VK_NULL_HANDLE };
417 
418  // Shared texture properties
419 #ifdef WIN32
420  HANDLE _sharedHandle { nullptr }; // Windows handle for shared texture memory.
421 #else
422  int _sharedFd { -1 }; // File descriptor is used for sharing memory between Vulkan and OpenGL on Linux.
423 #endif
424  GLuint _openGLMemoryObject = 0;
425  GLuint _openGLId = 0;
426  GLuint _openGLSourceId = 0;
427 };
428 
429 } }
430 
431 #endif
A simple object wrapper for an OpenGL texture.
Definition: material-networking/src/material-networking/TextureCache.h:39