Browse Source

Vk: overview docs for Memory, Buffer and Image.

pull/234/head
Vladimír Vondruš 6 years ago
parent
commit
e81f4d5224
  1. 106
      doc/snippets/MagnumVk.cpp
  2. 31
      src/Magnum/Vk/Buffer.h
  3. 32
      src/Magnum/Vk/Image.h
  4. 60
      src/Magnum/Vk/Memory.h

106
doc/snippets/MagnumVk.cpp

@ -25,10 +25,12 @@
#include <string> #include <string>
#include <Corrade/Containers/ArrayView.h> #include <Corrade/Containers/ArrayView.h>
#include <Corrade/Utility/Algorithms.h>
#include <Corrade/Utility/Directory.h> #include <Corrade/Utility/Directory.h>
#include "Magnum/Magnum.h" #include "Magnum/Magnum.h"
#include "Magnum/Math/Color.h" #include "Magnum/Math/Color.h"
#include "Magnum/Vk/Buffer.h"
#include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/CommandBuffer.h"
#include "Magnum/Vk/CommandPool.h" #include "Magnum/Vk/CommandPool.h"
#include "Magnum/Vk/Device.h" #include "Magnum/Vk/Device.h"
@ -37,6 +39,7 @@
#include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/ExtensionProperties.h"
#include "Magnum/Vk/Instance.h" #include "Magnum/Vk/Instance.h"
#include "Magnum/Vk/Integration.h" #include "Magnum/Vk/Integration.h"
#include "Magnum/Vk/Image.h"
#include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/LayerProperties.h"
#include "Magnum/Vk/Queue.h" #include "Magnum/Vk/Queue.h"
#include "Magnum/Vk/Shader.h" #include "Magnum/Vk/Shader.h"
@ -115,6 +118,35 @@ Vk::Device device{instance, std::move(info)};
/* [wrapping-optimizing-properties-device-move] */ /* [wrapping-optimizing-properties-device-move] */
} }
{
Vk::Device device{NoCreate};
/* [Buffer-usage] */
Vk::Buffer buffer{device,
Vk::BufferCreateInfo{Vk::BufferUsage::VertexBuffer, 1024*1024},
Vk::MemoryFlag::DeviceLocal
};
/* [Buffer-usage] */
}
{
Vk::Device device{NoCreate};
/* [Buffer-usage-custom-allocation] */
Vk::Buffer buffer{device,
Vk::BufferCreateInfo{Vk::BufferUsage::VertexBuffer, 1024*1024},
NoAllocate
};
Vk::MemoryRequirements requirements = buffer.memoryRequirements();
Vk::Memory memory{device, Vk::MemoryAllocateInfo{
requirements.size(),
device.properties().pickMemory(Vk::MemoryFlag::DeviceLocal,
requirements.memories())
}};
buffer.bindMemory(memory, 0);
/* [Buffer-usage-custom-allocation] */
}
{ {
/* [CommandPool-usage] */ /* [CommandPool-usage] */
Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; Vk::Device device{DOXYGEN_IGNORE(NoCreate)};
@ -211,6 +243,35 @@ if(device.isExtensionEnabled<Vk::Extensions::EXT::index_type_uint8>()) {
/* [Device-isExtensionEnabled] */ /* [Device-isExtensionEnabled] */
} }
{
Vk::Device device{NoCreate};
/* [Image-usage] */
Vk::Image image{device, Vk::ImageCreateInfo2D{
Vk::ImageUsage::Sampled, VK_FORMAT_R8G8B8A8_SRGB, {1024, 1024}, 1
}, Vk::MemoryFlag::DeviceLocal
};
/* [Image-usage] */
}
{
Vk::Device device{NoCreate};
/* [Image-usage-custom-allocation] */
Vk::Image image{device, Vk::ImageCreateInfo2D{
Vk::ImageUsage::Sampled, VK_FORMAT_R8G8B8A8_SRGB, {1024, 1024}, 1
}, NoAllocate
};
Vk::MemoryRequirements requirements = image.memoryRequirements();
Vk::Memory memory{device, Vk::MemoryAllocateInfo{
requirements.size(),
device.properties().pickMemory(Vk::MemoryFlag::DeviceLocal,
requirements.memories())
}};
image.bindMemory(memory, 0);
/* [Image-usage-custom-allocation] */
}
{ {
int argc{}; int argc{};
const char** argv{}; const char** argv{};
@ -310,6 +371,51 @@ if(instance.isExtensionEnabled<Vk::Extensions::EXT::debug_utils>()) {
/* [Instance-isExtensionEnabled] */ /* [Instance-isExtensionEnabled] */
} }
{
Vk::Device device{NoCreate};
Containers::ArrayView<const char> vertexData, indexData;
/* [Memory-usage] */
/* Create buffers without allocating them */
Vk::Buffer vertices{device,
Vk::BufferCreateInfo{Vk::BufferUsage::VertexBuffer, vertexData.size()},
NoAllocate};
Vk::Buffer indices{device,
Vk::BufferCreateInfo{Vk::BufferUsage::IndexBuffer, vertexData.size()},
NoAllocate};
/* Query memory requirements of both buffers, calculate max alignment */
Vk::MemoryRequirements verticesRequirements = vertices.memoryRequirements();
Vk::MemoryRequirements indicesRequirements = indices.memoryRequirements();
const UnsignedLong alignment = Math::max(verticesRequirements.alignment(),
indicesRequirements.alignment());
/* Allocate memory that's large enough to contain both buffers including
the strictest alignment, and is of a type satisfying requirements of both */
Vk::Memory memory{device, Vk::MemoryAllocateInfo{
verticesRequirements.alignedSize(alignment) +
indicesRequirements.alignedSize(alignment),
device.properties().pickMemory(Vk::MemoryFlag::HostVisible,
verticesRequirements.memories() & indicesRequirements.memories())
}};
const UnsignedLong indicesOffset = verticesRequirements.alignedSize(alignment);
/* Bind the respective sub-ranges to the buffers */
vertices.bindMemory(memory, 0);
indices.bindMemory(memory, indicesOffset);
/* [Memory-usage] */
/* [Memory-mapping] */
/* The memory gets unmapped again at the end of scope */
{
Containers::Array<char, Vk::MemoryMapDeleter> mapped = memory.map();
Utility::copy(vertexData, mapped.prefix(vertexData.size()));
Utility::copy(indexData,
mapped.slice(indicesOffset, indicesOffset + indexData.size()));
}
/* [Memory-mapping] */
}
{ {
Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; Vk::Device device{DOXYGEN_IGNORE(NoCreate)};
/* [Shader-usage] */ /* [Shader-usage] */

31
src/Magnum/Vk/Buffer.h

@ -159,7 +159,36 @@ CORRADE_ENUMSET_OPERATORS(BufferCreateInfo::Flags)
@brief Buffer @brief Buffer
@m_since_latest @m_since_latest
Wraps a @type_vk_keyword{Buffer}. Wraps a @type_vk_keyword{Buffer} and its memory.
@section Vk-Buffer-usage Basic usage
Pass a @ref BufferCreateInfo with desired usage and size to the @ref Buffer
constructor together with specifying @ref MemoryFlags for the allocation.
@snippet MagnumVk.cpp Buffer-usage
@attention At this point, a dedicated allocation is used, subsequently
accessible through @ref dedicatedMemory(). This behavior may change in the
future.
@section Vk-Buffer-usage-custom-allocation Custom memory allocation
Using @ref Buffer(Device&, const BufferCreateInfo&, NoAllocateT), the buffer
will be created without any memory bound. Buffer memory requirements can be
then queried using @ref memoryRequirements() and an allocated memory bound with
@ref bindMemory(). See @ref Memory for further details about memory allocation.
@snippet MagnumVk.cpp Buffer-usage-custom-allocation
Using @ref bindDedicatedMemory() instead of @ref bindMemory() will transfer
ownership of the @ref Memory to the buffer instance, making it subsequently
available through @ref dedicatedMemory(). This matches current behavior of the
@ref Buffer(Device&, const BufferCreateInfo&, MemoryFlags) constructor shown
above, except that you have more control over choosing and allocating the
memory.
@see @ref Image
*/ */
class MAGNUM_VK_EXPORT Buffer { class MAGNUM_VK_EXPORT Buffer {
public: public:

32
src/Magnum/Vk/Image.h

@ -333,7 +333,37 @@ class ImageCreateInfoCubeMapArray: public ImageCreateInfo {
@brief Image @brief Image
@m_since_latest @m_since_latest
Wraps a @type_vk_keyword{Image}. Wraps a @type_vk_keyword{Image} and its memory.
@section Vk-Image-usage Basic usage
Pass one of the @ref ImageCreateInfo subclasses depending on desired image type
with desired usage, format, size and other propoerties to the @ref Image
constructor together with specifying @ref MemoryFlags for memory allocation.
@snippet MagnumVk.cpp Image-usage
@attention At this point, a dedicated allocation is used, subsequently
accessible through @ref dedicatedMemory(). This behavior may change in the
future.
@section Vk-Image-usage-custom-allocation Custom memory allocation
Using @ref Image(Device&, const ImageCreateInfo&, NoAllocateT), the image will
be created without any memory attached. Image memory requirements can be
subsequently queried using @ref memoryRequirements() and an allocated memory
bound with @ref bindMemory(). See @ref Memory for further details about memory allocation.
@snippet MagnumVk.cpp Image-usage-custom-allocation
Using @ref bindDedicatedMemory() instead of @ref bindMemory() will transfer
ownership of the @ref Memory to the image instance, making it subsequently
available through @ref dedicatedMemory(). This matches current behavior of the
@ref Image(Device&, const ImageCreateInfo&, MemoryFlags) constructor shown
above, except that you have more control over choosing and allocating the
memory.
@see @ref Buffer
*/ */
class MAGNUM_VK_EXPORT Image { class MAGNUM_VK_EXPORT Image {
public: public:

60
src/Magnum/Vk/Memory.h

@ -267,7 +267,65 @@ class MAGNUM_VK_EXPORT MemoryAllocateInfo {
@brief Device memory @brief Device memory
@m_since_latest @m_since_latest
Wraps a @type_vk_keyword{DeviceMemory}. Wraps a @type_vk_keyword{DeviceMemory} and handles its allocation and mapping.
@section Vk-Memory-usage Usage
By default, the memory will get allocated for you during the creation of
@ref Buffer, @ref Image and other objects. In case you want to handle the
allocation yourself instead (which you indicate by passing the @ref NoAllocate
tag to constructors of these objects), it consists of these steps:
1. Querying memory requirements of a particular object, for example using
@ref Buffer::memoryRequirements() or @ref Image::memoryRequirements()
2. Picking a memory type satisfying requirements of the object it's being
allocated for (such as allowed memory types) and user requirements (whether
it should be device-local, host-mappable etc.) using
@ref DeviceProperties::pickMemory()
3. Allocating a new @ref Memory or taking a (correctly aligned) sub-range of
an existing allocation from given memory type
4. Binding the memory (sub-range) to the object, using
@ref Buffer::bindMemory(), @ref Image::bindMemory() and others
The following example allocates a single block memory for two buffers, one
containing vertex and the other index data:
@snippet MagnumVk.cpp Memory-usage
@section Vk-Memory-mapping Memory mapping
If the memory is created with the @ref MemoryFlag::HostVisible flag, it can be
mapped on the host via @ref map(). The unmapping is then taken care of by a
custom deleter in the returned @ref Corrade::Containers::Array. It's possible
to map either the whole range or a sub-range, however note that one @ref Memory
object can't be mapped twice at the same time --- in the code snippet above, it
means that in order to upload vertex and index data, there are two options:
- One is to first map the vertex buffer sub-range, upload the data, unmap it,
and then do the same process for the index buffer sub-range. This way is
more encapsulated without having to worry if there's already a mapping and
who owns it, but means more work for the driver.
- Another option is to map the whole memory at once and then upload data of
particular buffers to correct subranges. Here the mapping has to be owned
by some external entity which ensures it's valid for as long as any buffer
wants to map its memory sub-range.
The following example maps the memory allocated above and copies index and
vertex data to it:
@snippet MagnumVk.cpp Memory-mapping
<b></b>
@m_class{m-note m-success}
@par Map temporarily or forever?
Mapping smaller ranges and unmapping again after makes sense on 32-bit
systems where the amount of virtual memory is limited --- otherwise it may
happen that the system won't be able to find a sufficiently large block of
virtual memory, causing the next mapping to fail. On 64-bit systems the
virtual address space is sufficiently large for most use cases and it's
common to just map the whole memory block for its whole lifetime.
*/ */
class MAGNUM_VK_EXPORT Memory { class MAGNUM_VK_EXPORT Memory {
public: public:

Loading…
Cancel
Save