Skip to content

Commit 9277726

Browse files
committed
Cache uploaded redundantly uploaded textures with P_State
Will optimize the case where either the source-layer is either a static image or a low-framerate gif by identifying the uploaded contents of the image with a P_State and re-using the uploaded image rather than uploading it each frame.
1 parent debc048 commit 9277726

2 files changed

Lines changed: 124 additions & 86 deletions

File tree

include/Vulkanator.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <AEConfig.h>
99

1010
#include <AE_Effect.h>
11+
#include <AE_EffectSuites.h>
1112
#include <entry.h>
1213

1314
#include "VulkanConfig.hpp"
@@ -108,6 +109,7 @@ struct SequenceParams
108109
vk::ImageCreateInfo InputImageInfoCache = {};
109110
vk::ImageCreateInfo OutputImageInfoCache = {};
110111

112+
PF_State InputImageState = {};
111113
vk::UniqueImage InputImage = {};
112114
vk::UniqueDeviceMemory InputImageMemory = {};
113115

source/Vulkanator.cpp

Lines changed: 122 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,24 @@ PF_Err SmartRender(
12831283
InputImageInfo.sharingMode = vk::SharingMode::eExclusive;
12841284
InputImageInfo.initialLayout = vk::ImageLayout::eUndefined;
12851285

1286-
if( InputImageInfo == SequenceParam->Cache.InputImageInfoCache )
1286+
// Get "hash" of current input image
1287+
const A_Time CurTime = {in_data->current_time, in_data->time_scale};
1288+
const A_Time CurTimeStep = {in_data->time_step, in_data->time_scale};
1289+
1290+
PF_State CurState = {};
1291+
ERR(suites.ParamUtilsSuite3()->PF_GetCurrentState(
1292+
in_data->effect_ref, Vulkanator::ParamID::Input, &CurTime, &CurTimeStep,
1293+
&CurState
1294+
));
1295+
1296+
// Compare hash of the currently uploaded texture against the cached one
1297+
A_Boolean InputImageStateIsSame = false;
1298+
ERR(suites.ParamUtilsSuite3()->PF_AreStatesIdentical(
1299+
in_data->effect_ref, &SequenceParam->Cache.InputImageState, &CurState,
1300+
&InputImageStateIsSame
1301+
));
1302+
1303+
if( InputImageStateIsSame )
12871304
{
12881305
// Cache Hit
12891306
}
@@ -1300,19 +1317,21 @@ PF_Err SmartRender(
13001317
)
13011318
.value();
13021319
SequenceParam->Cache.InputImageInfoCache = InputImageInfo;
1320+
SequenceParam->Cache.InputImageState = CurState;
13031321
}
13041322

1305-
// This provides a mapping between the image contents and the staging buffer
1323+
// This provides a mapping between the image contents and the staging
1324+
// buffer
13061325
const vk::BufferImageCopy InputBufferMapping(
13071326
0, std::uint32_t(InputLayer->rowbytes / PixelSize), 0,
13081327
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1),
13091328
vk::Offset3D(0, 0, 0),
13101329
vk::Extent3D(InputLayer->width, InputLayer->height, 1)
13111330
);
13121331

1313-
// Input image view, this is used to create an interpretation of a certain
1314-
// aspect of the image This allows things like having a 2D image array but
1315-
// creating a view around just one of the images
1332+
// Input image view, this is used to create an interpretation of a
1333+
// certain aspect of the image This allows things like having a 2D image
1334+
// array but creating a view around just one of the images
13161335
vk::ImageViewCreateInfo InputImageViewInfo = {};
13171336
// The target image we are making a view of
13181337
InputImageViewInfo.image = SequenceParam->Cache.InputImage.get();
@@ -1324,8 +1343,8 @@ PF_Err SmartRender(
13241343
InputImageViewInfo.components.b = vk::ComponentSwizzle::eIdentity;
13251344
InputImageViewInfo.components.a = vk::ComponentSwizzle::eIdentity;
13261345
InputImageViewInfo.subresourceRange = vk::ImageSubresourceRange(
1327-
vk::ImageAspectFlagBits::eColor, // We want the "Color" aspect of the
1328-
// image
1346+
vk::ImageAspectFlagBits::eColor, // We want the "Color" aspect of
1347+
// the image
13291348
0, 1, // A single mipmap, mipmap 0
13301349
0, 1 // A single image layer, layer 0
13311350
);
@@ -1356,11 +1375,12 @@ PF_Err SmartRender(
13561375
OutputImageInfo.samples = vk::SampleCountFlagBits::e1;
13571376
OutputImageInfo.tiling = vk::ImageTiling::eOptimal;
13581377
OutputImageInfo.usage
1359-
= vk::ImageUsageFlagBits::eTransferSrc // Will be transferring from this
1360-
// image into the staging buffer
1361-
| vk::ImageUsageFlagBits::eColorAttachment; // Will be rendering into
1362-
// this image within a
1363-
// render pass
1378+
= vk::ImageUsageFlagBits::eTransferSrc // Will be transferring from
1379+
// this image into the
1380+
// staging buffer
1381+
| vk::ImageUsageFlagBits::eColorAttachment; // Will be rendering
1382+
// into this image
1383+
// within a render pass
13641384
OutputImageInfo.sharingMode = vk::SharingMode::eExclusive;
13651385
OutputImageInfo.initialLayout = vk::ImageLayout::eUndefined;
13661386

@@ -1383,17 +1403,18 @@ PF_Err SmartRender(
13831403
SequenceParam->Cache.OutputImageInfoCache = OutputImageInfo;
13841404
}
13851405

1386-
// This provides a mapping between the image contents and the staging buffer
1406+
// This provides a mapping between the image contents and the staging
1407+
// buffer
13871408
const vk::BufferImageCopy OutputBufferMapping(
13881409
0, std::uint32_t(OutputLayer->rowbytes / PixelSize), 0,
13891410
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1),
13901411
vk::Offset3D(0, 0, 0),
13911412
vk::Extent3D(OutputLayer->width, OutputLayer->height, 1)
13921413
);
13931414

1394-
// Output image view, this is used to create an interpretation of a certain
1395-
// aspect of the image This allows things like having a 2D image array but
1396-
// creating a view around just one of the images
1415+
// Output image view, this is used to create an interpretation of a
1416+
// certain aspect of the image This allows things like having a 2D image
1417+
// array but creating a view around just one of the images
13971418
vk::ImageViewCreateInfo OutputImageViewInfo = {};
13981419
// The target image we are making a view of
13991420
OutputImageViewInfo.image = SequenceParam->Cache.OutputImage.get();
@@ -1405,8 +1426,8 @@ PF_Err SmartRender(
14051426
OutputImageViewInfo.components.b = vk::ComponentSwizzle::eIdentity;
14061427
OutputImageViewInfo.components.a = vk::ComponentSwizzle::eIdentity;
14071428
OutputImageViewInfo.subresourceRange = vk::ImageSubresourceRange(
1408-
vk::ImageAspectFlagBits::eColor, // We want the "Color" aspect of the
1409-
// image
1429+
vk::ImageAspectFlagBits::eColor, // We want the "Color" aspect of
1430+
// the image
14101431
0, 1, // A single mipmap, mipmap 0
14111432
0, 1 // A single image layer, layer 0
14121433
);
@@ -1462,10 +1483,10 @@ PF_Err SmartRender(
14621483
}
14631484

14641485
// Write combined image+sampler object into the descriptor set
1465-
// Here, we combine both the sampler and the image, and we state the format
1466-
// that the image will be in by the time this sampler will be in-use, which
1467-
// is ideally "shader read only optimal" immediately after we are done
1468-
// uploading the texture to the GPU
1486+
// Here, we combine both the sampler and the image, and we state the
1487+
// format that the image will be in by the time this sampler will be
1488+
// in-use, which is ideally "shader read only optimal" immediately after
1489+
// we are done uploading the texture to the GPU
14691490
vk::DescriptorImageInfo InputImageSamplerWrite(
14701491
FrameParam->InputImageSampler.get(), InputImageView.get(),
14711492
vk::ImageLayout::eShaderReadOnlyOptimal
@@ -1485,14 +1506,14 @@ PF_Err SmartRender(
14851506
{}
14861507
);
14871508

1488-
// Create Render pass Framebuffer, this maps the Output buffer as a color
1489-
// attachment for a Renderpass to render into You can add more attachments
1490-
// of different formats, but they must all have the same width,height,layers
1491-
// Framebuffers will define the image data that render passes will be able
1492-
// to address in total
1509+
// Create Render pass Framebuffer, this maps the Output buffer as a
1510+
// color attachment for a Renderpass to render into You can add more
1511+
// attachments of different formats, but they must all have the same
1512+
// width,height,layers Framebuffers will define the image data that
1513+
// render passes will be able to address in total
14931514
vk::FramebufferCreateInfo OutputFramebufferInfo = {};
1494-
// This is for the framebuffer to know what ~~~compatible~~~ renderpasses
1495-
// will be rendered into it
1515+
// This is for the framebuffer to know what ~~~compatible~~~
1516+
// renderpasses will be rendered into it
14961517
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#renderpass-compatibility
14971518
OutputFramebufferInfo.renderPass
14981519
= GlobalParam->RenderPasses[FrameParam->Uniforms.Depth].get();
@@ -1518,8 +1539,8 @@ PF_Err SmartRender(
15181539
return PF_Err_INTERNAL_STRUCT_DAMAGED;
15191540
}
15201541

1521-
// Copy Input image data into staging buffer, but keep it mapped, as we will
1522-
// read the output image data from it later too
1542+
// Copy Input image data into staging buffer, but keep it mapped, as we
1543+
// will read the output image data from it later too
15231544
void* StagingBufferMapping = nullptr;
15241545

15251546
if( auto MapResult = GlobalParam->Device->mapMemory(
@@ -1535,12 +1556,16 @@ PF_Err SmartRender(
15351556
return PF_Err_INTERNAL_STRUCT_DAMAGED;
15361557
}
15371558

1538-
// Copy into staging buffer
1539-
std::memcpy(
1540-
StagingBufferMapping, InputLayer->data,
1541-
InputLayer->rowbytes * InputLayer->height
1542-
);
1559+
if( !InputImageStateIsSame )
1560+
{
1561+
// Copy Input Image into staging buffer
1562+
std::memcpy(
1563+
StagingBufferMapping, InputLayer->data,
1564+
InputLayer->rowbytes * InputLayer->height
1565+
);
1566+
}
15431567

1568+
// Upload uniform data
15441569
if( auto MapResult = GlobalParam->Device->mapMemory(
15451570
SequenceParam->UniformBufferMemory.get(), 0, VK_WHOLE_SIZE
15461571
);
@@ -1581,58 +1606,69 @@ PF_Err SmartRender(
15811606
{
15821607
////// Upload staging buffer into Input Image
15831608

1584-
// Layout transitions, prepare to copy
1585-
// Transfer buffers into images
1586-
Cmd.pipelineBarrier(
1587-
vk::PipelineStageFlagBits::eHost,
1588-
vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlags(), {},
1589-
{// Get staging buffer ready for a read
1590-
vk::BufferMemoryBarrier(
1591-
vk::AccessFlags(), vk::AccessFlagBits::eTransferRead,
1592-
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
1593-
SequenceParam->Cache.StagingBuffer.get(), 0u, VK_WHOLE_SIZE
1594-
)},
1595-
{
1596-
// Get Input Image ready to be written to
1597-
vk::ImageMemoryBarrier(
1598-
vk::AccessFlags(), vk::AccessFlagBits::eTransferWrite,
1599-
vk::ImageLayout::eUndefined,
1600-
vk::ImageLayout::eTransferDstOptimal,
1601-
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
1602-
SequenceParam->Cache.InputImage.get(),
1603-
vk::ImageSubresourceRange(
1604-
vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1
1605-
)
1606-
),
1607-
}
1608-
);
1609+
if( !InputImageStateIsSame )
1610+
{
1611+
// Layout transitions, prepare to copy
1612+
// Transfer buffers into images
1613+
Cmd.pipelineBarrier(
1614+
vk::PipelineStageFlagBits::eHost,
1615+
vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlags(), {},
1616+
{// Get staging buffer ready for a read
1617+
vk::BufferMemoryBarrier(
1618+
vk::AccessFlags(), vk::AccessFlagBits::eTransferRead,
1619+
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
1620+
SequenceParam->Cache.StagingBuffer.get(), 0u, VK_WHOLE_SIZE
1621+
)},
1622+
{
1623+
// Get Input Image ready to be written to
1624+
vk::ImageMemoryBarrier(
1625+
vk::AccessFlags(), vk::AccessFlagBits::eTransferWrite,
1626+
vk::ImageLayout::eUndefined,
1627+
vk::ImageLayout::eTransferDstOptimal,
1628+
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
1629+
SequenceParam->Cache.InputImage.get(),
1630+
vk::ImageSubresourceRange(
1631+
vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1
1632+
)
1633+
),
1634+
}
1635+
);
16091636

1610-
// Upload input image data from staging buffer into Input Image
1611-
Cmd.copyBufferToImage(
1612-
SequenceParam->Cache.StagingBuffer.get(),
1613-
SequenceParam->Cache.InputImage.get(),
1614-
vk::ImageLayout::eTransferDstOptimal, {InputBufferMapping}
1615-
);
1637+
// Upload input image data from staging buffer into Input Image
1638+
Cmd.copyBufferToImage(
1639+
SequenceParam->Cache.StagingBuffer.get(),
1640+
SequenceParam->Cache.InputImage.get(),
1641+
vk::ImageLayout::eTransferDstOptimal, {InputBufferMapping}
1642+
);
16161643

1617-
// Layout transitions, copy is complete, ready input image to be sampled
1618-
// from
1644+
// Layout transitions, copy is complete, ready input image to be
1645+
// sampled from
1646+
Cmd.pipelineBarrier(
1647+
vk::PipelineStageFlagBits::eTransfer,
1648+
vk::PipelineStageFlagBits::eComputeShader,
1649+
vk::DependencyFlags(), {}, {},
1650+
{// Input Image is going to be read
1651+
vk::ImageMemoryBarrier(
1652+
vk::AccessFlagBits::eTransferWrite,
1653+
vk::AccessFlagBits::eShaderRead,
1654+
vk::ImageLayout::eTransferDstOptimal,
1655+
vk::ImageLayout::eShaderReadOnlyOptimal,
1656+
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
1657+
SequenceParam->Cache.InputImage.get(),
1658+
vk::ImageSubresourceRange(
1659+
vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1
1660+
)
1661+
)}
1662+
);
1663+
}
1664+
1665+
// Layout transitions, copy is complete, ready input image to be
1666+
// sampled from
16191667
Cmd.pipelineBarrier(
16201668
vk::PipelineStageFlagBits::eTransfer,
16211669
vk::PipelineStageFlagBits::eComputeShader, vk::DependencyFlags(),
16221670
{}, {},
1623-
{// Input Image is going to be read
1624-
vk::ImageMemoryBarrier(
1625-
vk::AccessFlagBits::eTransferWrite,
1626-
vk::AccessFlagBits::eShaderRead,
1627-
vk::ImageLayout::eTransferDstOptimal,
1628-
vk::ImageLayout::eShaderReadOnlyOptimal,
1629-
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
1630-
SequenceParam->Cache.InputImage.get(),
1631-
vk::ImageSubresourceRange(
1632-
vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1
1633-
)
1634-
),
1635-
// Output Image is going to be written to as a color attachment
1671+
{// Output Image is going to be written to as a color attachment
16361672
// within a render pass
16371673
vk::ImageMemoryBarrier(
16381674
vk::AccessFlags(), vk::AccessFlagBits::eShaderWrite,
@@ -1658,10 +1694,10 @@ PF_Err SmartRender(
16581694
BeginInfo.framebuffer = OutputFramebuffer.get();
16591695

16601696
// Rectangular region of the output buffer to render into
1661-
// TODO: we could potentially have a cached layer-sized output image,
1662-
// and only render into a subset of this image using extent_hint if we
1663-
// wanted to. But we use the exact output size for more immediate memory
1664-
// savings
1697+
// TODO: we could potentially have a cached layer-sized output
1698+
// image, and only render into a subset of this image using
1699+
// extent_hint if we wanted to. But we use the exact output size for
1700+
// more immediate memory savings
16651701
BeginInfo.renderArea.offset.x = BeginInfo.renderArea.offset.y = 0;
16661702
BeginInfo.renderArea.extent.width = std::uint32_t(OutputLayer->width);
16671703
BeginInfo.renderArea.extent.height = std::uint32_t(OutputLayer->height);

0 commit comments

Comments
 (0)