Compare commits
14 Commits
ebridgewat
...
jc/adjustT
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d06afc9a57 | ||
|
|
04f7837439 | ||
|
|
85d00831bc | ||
|
|
4d8f92f087 | ||
|
|
5850a7eee4 | ||
|
|
1b2620cdf3 | ||
|
|
ddca795cc9 | ||
|
|
3a5f558874 | ||
|
|
13629ed93a | ||
|
|
b129e5df4a | ||
|
|
2abacaa030 | ||
|
|
9a88537e08 | ||
|
|
9640f2656f | ||
|
|
2811b064a9 |
33
.github/workflows/presubmit.yml
vendored
33
.github/workflows/presubmit.yml
vendored
@@ -130,7 +130,8 @@ jobs:
|
||||
run: |
|
||||
bash build/common/get-mesa.sh
|
||||
pip install tifffile numpy
|
||||
- name: Render
|
||||
- name: Render and compare
|
||||
id: render_compare
|
||||
run: |
|
||||
TEST_DIR=test/renderdiff
|
||||
source ${TEST_DIR}/src/preamble.sh
|
||||
@@ -140,20 +141,32 @@ jobs:
|
||||
python3 ${TEST_DIR}/src/golden_manager.py \
|
||||
--branch=${GOLDEN_BRANCH} \
|
||||
--output=${GOLDEN_OUTPUT_DIR}
|
||||
# Note that we need to upload the output even if comparison fails
|
||||
|
||||
# Note that we need to upload the output even if comparison fails, so we undo `set -ex`
|
||||
end_
|
||||
|
||||
python3 ${TEST_DIR}/src/compare.py \
|
||||
--src=${GOLDEN_OUTPUT_DIR} \
|
||||
--dest=${RENDER_OUTPUT_DIR} \
|
||||
--out=${DIFF_OUTPUT_DIR} 2>&1 | tee compare_output.txt
|
||||
|
||||
if grep "Failed" compare_output.txt > /dev/null; then
|
||||
DELIMITER="EOF_FILE_CONTENT_$(date +%s)" # Using timestamp to make it more unique
|
||||
echo "err<<$DELIMITER" >> "$GITHUB_OUTPUT"
|
||||
cat compare_output.txt >> "$GITHUB_OUTPUT"
|
||||
echo "$DELIMITER" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: presubmit-renderdiff-result
|
||||
path: ./out/renderdiff
|
||||
- name: Compare output
|
||||
- name: Compare result
|
||||
run: |
|
||||
TEST_DIR=test/renderdiff
|
||||
source ${TEST_DIR}/src/preamble.sh
|
||||
python3 ${TEST_DIR}/src/compare.py \
|
||||
--src=${GOLDEN_OUTPUT_DIR} \
|
||||
--dest=${RENDER_OUTPUT_DIR} \
|
||||
--out=${DIFF_OUTPUT_DIR}
|
||||
end_
|
||||
ERROR_STR="${{ steps.render_compare.outputs.err }}"
|
||||
if [ -n "${ERROR_STR}" ]; then
|
||||
echo "${ERROR_STR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
validate-wgsl-webgpu:
|
||||
name: validate-wgsl-webgpu
|
||||
|
||||
@@ -134,12 +134,14 @@ set(SRCS
|
||||
src/details/Texture.cpp
|
||||
src/details/VertexBuffer.cpp
|
||||
src/details/View.cpp
|
||||
src/ds/PerViewDescriptorSetUtils.cpp
|
||||
src/ds/ColorPassDescriptorSet.cpp
|
||||
src/ds/DescriptorSet.cpp
|
||||
src/ds/DescriptorSetLayout.cpp
|
||||
src/ds/PostProcessDescriptorSet.cpp
|
||||
src/ds/ShadowMapDescriptorSet.cpp
|
||||
src/ds/SsrPassDescriptorSet.cpp
|
||||
src/ds/StructureDescriptorSet.cpp
|
||||
src/fg/Blackboard.cpp
|
||||
src/fg/DependencyGraph.cpp
|
||||
src/fg/FrameGraph.cpp
|
||||
|
||||
@@ -193,6 +193,13 @@ public:
|
||||
* - PlatformEGL
|
||||
*/
|
||||
GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT;
|
||||
|
||||
/**
|
||||
* Bypass the staging buffer because the device is of Unified Memory Architecture.
|
||||
* This is only supported for:
|
||||
* - VulkanPlatform
|
||||
*/
|
||||
bool vulkanEnableStagingBufferBypass = false;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
|
||||
@@ -88,7 +88,8 @@ public:
|
||||
|
||||
// sets the material name and variant for diagnostic purposes only
|
||||
Program& diagnostics(utils::CString const& name,
|
||||
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)>&& logger);
|
||||
utils::Invocable<utils::io::ostream&(utils::CString const& name,
|
||||
utils::io::ostream& out)>&& logger);
|
||||
|
||||
// Sets one of the program's shader (e.g. vertex, fragment)
|
||||
// string-based shaders are null terminated, consequently the size parameter must include the
|
||||
@@ -173,7 +174,8 @@ private:
|
||||
utils::CString mName;
|
||||
uint64_t mCacheId{};
|
||||
CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH;
|
||||
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)> mLogger;
|
||||
utils::Invocable<utils::io::ostream&(utils::CString const& name, utils::io::ostream& out)>
|
||||
mLogger;
|
||||
SpecializationConstantsInfo mSpecializationConstants;
|
||||
std::array<utils::FixedCapacityVector<PushConstant>, SHADER_TYPE_COUNT> mPushConstants;
|
||||
DescriptorSetInfo mDescriptorBindings;
|
||||
|
||||
@@ -45,7 +45,7 @@ Program& Program::priorityQueue(CompilerPriorityQueue priorityQueue) noexcept {
|
||||
}
|
||||
|
||||
Program& Program::diagnostics(CString const& name,
|
||||
Invocable<io::ostream&(io::ostream&)>&& logger) {
|
||||
Invocable<io::ostream&(utils::CString const& name, io::ostream&)>&& logger) {
|
||||
mName = name;
|
||||
mLogger = std::move(logger);
|
||||
return *this;
|
||||
@@ -103,7 +103,7 @@ Program& Program::multiview(bool multiview) noexcept {
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, const Program& builder) {
|
||||
out << "Program{";
|
||||
builder.mLogger(out);
|
||||
builder.mLogger(builder.mName, out);
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -20,33 +20,30 @@
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanBufferProxy::VulkanBufferProxy(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
VulkanBufferCache& bufferCache, VulkanBufferUsage usage, uint32_t numBytes)
|
||||
: mAllocator(allocator),
|
||||
VulkanBufferProxy::VulkanBufferProxy(VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, VulkanBufferUsage usage,
|
||||
uint32_t numBytes)
|
||||
: mStagingBufferBypassEnabled(context.stagingBufferBypassEnabled()),
|
||||
mAllocator(allocator),
|
||||
mStagePool(stagePool),
|
||||
mBufferCache(bufferCache),
|
||||
mBuffer(mBufferCache.acquire(usage, numBytes)),
|
||||
mUpdatedOffset(0),
|
||||
mUpdatedBytes(0) {}
|
||||
mLastReadAge(0) {}
|
||||
|
||||
void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData,
|
||||
uint32_t byteOffset, uint32_t numBytes) {
|
||||
// The VulkanBuffer is available if the only object reference is hold by the
|
||||
// `VulkanBufferProxy`. This means that the buffer is not currently referenced by a
|
||||
// `VulkanCommandBuffer`.
|
||||
bool const isAvailable = mBuffer->getCount() == 1;
|
||||
|
||||
if (isAvailable) {
|
||||
// We are up to date with all the update operations, so no need to synchronize with previous
|
||||
// updates.
|
||||
mUpdatedBytes = 0;
|
||||
mUpdatedOffset = 0;
|
||||
}
|
||||
// This means that we're recording a write into a command buffer without a previous read, so it
|
||||
// should be safe to
|
||||
// 1) Do a direct memcpy in UMA mode
|
||||
// 2) Skip adding a barrier (to protect the write from writing over a read).
|
||||
bool const isAvailable = commands.age() != mLastReadAge;
|
||||
|
||||
// Keep track of the VulkanBuffer usage
|
||||
commands.acquire(mBuffer);
|
||||
@@ -57,8 +54,7 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
|
||||
// buffer.
|
||||
bool const isMemcopyable = mBuffer->getGpuBuffer()->allocationInfo.pMappedData != nullptr;
|
||||
bool const isUniform = getUsage() == VulkanBufferUsage::UNIFORM;
|
||||
bool const useMemcpy =
|
||||
isUniform && isMemcopyable && isAvailable && !FVK_FORCE_STAGING_FOR_BUFFER_UPDATES;
|
||||
bool const useMemcpy = isUniform && isMemcopyable && isAvailable && mStagingBufferBypassEnabled;
|
||||
if (useMemcpy) {
|
||||
char* dest = static_cast<char*>(mBuffer->getGpuBuffer()->allocationInfo.pMappedData) +
|
||||
byteOffset;
|
||||
@@ -66,6 +62,10 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
|
||||
vmaFlushAllocation(mAllocator, mBuffer->getGpuBuffer()->vmaAllocation, byteOffset,
|
||||
numBytes);
|
||||
return;
|
||||
|
||||
// TODO: to properly bypass staging buffer, we'd need to be able to swap out a VulkanBuffer,
|
||||
// which represents a VkBuffer. This means that the corresponding descriptor sets also have
|
||||
// to be updated.
|
||||
}
|
||||
|
||||
// Note: this should be stored within the command buffer before going out of
|
||||
@@ -76,10 +76,9 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
|
||||
memcpy(stage->mapping(), cpuData, numBytes);
|
||||
vmaFlushAllocation(mAllocator, stage->memory(), stage->offset(), numBytes);
|
||||
|
||||
// If there was a previous update, then we need to make sure the following write is properly
|
||||
// If there was a previous read, then we need to make sure the following write is properly
|
||||
// synced with the previous read.
|
||||
if (mUpdatedBytes > 0 &&
|
||||
(byteOffset >= mUpdatedOffset && byteOffset <= (mUpdatedOffset + mUpdatedBytes))) {
|
||||
if (!isAvailable) {
|
||||
VkAccessFlags srcAccess = 0;
|
||||
VkPipelineStageFlags srcStage = 0;
|
||||
if (getUsage() == VulkanBufferUsage::UNIFORM) {
|
||||
@@ -114,9 +113,6 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
|
||||
};
|
||||
vkCmdCopyBuffer(commands.buffer(), stage->buffer(), getVkBuffer(), 1, ®ion);
|
||||
|
||||
mUpdatedOffset = byteOffset;
|
||||
mUpdatedBytes = numBytes;
|
||||
|
||||
// Firstly, ensure that the copy finishes before the next draw call.
|
||||
// Secondly, in case the user decides to upload another chunk (without ever using the first one)
|
||||
// we need to ensure that this upload completes first (hence
|
||||
@@ -160,4 +156,9 @@ VulkanBufferUsage VulkanBufferProxy::getUsage() const noexcept {
|
||||
return mBuffer->getGpuBuffer()->usage;
|
||||
}
|
||||
|
||||
void VulkanBufferProxy::referencedBy(VulkanCommandBuffer& commands) {
|
||||
commands.acquire(mBuffer);
|
||||
mLastReadAge = commands.age();
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -25,28 +25,35 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct VulkanDescriptorSet;
|
||||
struct VulkanCommandBuffer;
|
||||
|
||||
// This class acts as a dynamic wrapper for a `VulkanBuffer`. It allows you to modify the
|
||||
// `VulkanBuffer` it references at runtime, wihtout affecting any external objects.
|
||||
class VulkanBufferProxy {
|
||||
public:
|
||||
VulkanBufferProxy(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
VulkanBufferCache& bufferCache, VulkanBufferUsage usage, uint32_t numBytes);
|
||||
VulkanBufferProxy(VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, VulkanBufferUsage usage,
|
||||
uint32_t numBytes);
|
||||
|
||||
void loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData, uint32_t byteOffset,
|
||||
uint32_t numBytes);
|
||||
|
||||
VkBuffer getVkBuffer() const noexcept;
|
||||
|
||||
VulkanBufferUsage getUsage() const noexcept;
|
||||
void referencedBy(VulkanCommandBuffer& commands);
|
||||
|
||||
private:
|
||||
VulkanBufferUsage getUsage() const noexcept;
|
||||
|
||||
bool const mStagingBufferBypassEnabled;
|
||||
VmaAllocator mAllocator;
|
||||
VulkanStagePool& mStagePool;
|
||||
VulkanBufferCache& mBufferCache;
|
||||
|
||||
fvkmemory::resource_ptr<VulkanBuffer> mBuffer;
|
||||
uint32_t mUpdatedOffset = 0;
|
||||
uint32_t mUpdatedBytes = 0;
|
||||
|
||||
uint32_t mLastReadAge;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -86,6 +86,8 @@ bool VulkanGroupMarkers::empty() const noexcept {
|
||||
}
|
||||
#endif // FVK_DEBUG_GROUP_MARKERS
|
||||
|
||||
uint32_t VulkanCommandBuffer::sAgeCounter = 0;
|
||||
|
||||
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice device,
|
||||
VkQueue queue, VkCommandPool pool, bool isProtected)
|
||||
: mContext(context),
|
||||
@@ -94,7 +96,8 @@ VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice
|
||||
mDevice(device),
|
||||
mQueue(queue),
|
||||
mBuffer(createCommandBuffer(device, pool)),
|
||||
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)) {
|
||||
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)),
|
||||
mAge(++sAgeCounter) {
|
||||
VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
||||
vkCreateSemaphore(mDevice, &sci, VKALLOC, &mSubmission);
|
||||
|
||||
@@ -111,6 +114,7 @@ void VulkanCommandBuffer::reset() noexcept {
|
||||
mMarkerCount = 0;
|
||||
mResources.clear();
|
||||
mWaitSemaphores.clear();
|
||||
mAge = ++sAgeCounter;
|
||||
|
||||
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
|
||||
// gets, gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
|
||||
|
||||
@@ -108,7 +108,13 @@ struct VulkanCommandBuffer {
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
uint32_t age() const {
|
||||
return mAge;
|
||||
}
|
||||
|
||||
private:
|
||||
static uint32_t sAgeCounter;
|
||||
|
||||
VulkanContext const& mContext;
|
||||
uint8_t mMarkerCount;
|
||||
bool const isProtected;
|
||||
@@ -120,6 +126,7 @@ private:
|
||||
VkFence mFence;
|
||||
std::shared_ptr<VulkanCmdFence> mFenceStatus;
|
||||
std::vector<fvkmemory::resource_ptr<Resource>> mResources;
|
||||
uint32_t mAge;
|
||||
};
|
||||
|
||||
struct CommandBufferPool {
|
||||
|
||||
@@ -214,8 +214,4 @@ constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS;
|
||||
// destroying any unused pipeline object.
|
||||
static_assert(FVK_MAX_PIPELINE_AGE >= FVK_MAX_COMMAND_BUFFERS);
|
||||
|
||||
// Whether the buffer updates always use staging or not. Otherwise its allowed to also use memcpy
|
||||
// for buffer updates. Default is false
|
||||
constexpr static const bool FVK_FORCE_STAGING_FOR_BUFFER_UPDATES = false;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -146,6 +146,10 @@ public:
|
||||
return mIsUnifiedMemoryArchitecture;
|
||||
}
|
||||
|
||||
inline bool stagingBufferBypassEnabled() const noexcept {
|
||||
return mStagingBufferBypassEnabled;
|
||||
}
|
||||
|
||||
private:
|
||||
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
|
||||
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
|
||||
@@ -169,6 +173,7 @@ private:
|
||||
bool mLazilyAllocatedMemorySupported = false;
|
||||
bool mProtectedMemorySupported = false;
|
||||
bool mIsUnifiedMemoryArchitecture = false;
|
||||
bool mStagingBufferBypassEnabled = false;
|
||||
|
||||
fvkutils::VkFormatList mDepthStencilFormats;
|
||||
fvkutils::VkFormatList mBlittableDepthStencilFormats;
|
||||
|
||||
@@ -290,7 +290,9 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
||||
if (mLastBoundInfo.pipelineLayout == pipelineLayout) {
|
||||
auto& lastBoundSets = mLastBoundInfo.boundSets;
|
||||
curMask.forEachSetBit([&](size_t index) {
|
||||
if (updateSets[index] == lastBoundSets[index] && !useExternalSamplers[index]) {
|
||||
auto& set = updateSets[index];
|
||||
if (set == lastBoundSets[index] && !useExternalSamplers[index] &&
|
||||
set->uniqueDynamicUboCount == 0) {
|
||||
curMask.unset(index);
|
||||
}
|
||||
});
|
||||
@@ -302,25 +304,24 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||
VkDescriptorSet vkset = useExternalSamplers[index] ? set->getExternalSamplerVkSet() :
|
||||
set->getVkSet();
|
||||
commands->acquire(set);
|
||||
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
||||
1, &vkset, set->uniqueDynamicUboCount, set->getOffsets()->data());
|
||||
commands->acquire(set);
|
||||
set->referencedBy(*commands);
|
||||
});
|
||||
|
||||
mStashedSets = {};
|
||||
|
||||
mLastBoundInfo = {
|
||||
pipelineLayout,
|
||||
setMask,
|
||||
updateSets,
|
||||
};
|
||||
mStashedSets = {};
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
|
||||
VkDeviceSize offset, VkDeviceSize size) noexcept {
|
||||
VkDescriptorBufferInfo const info = {
|
||||
.buffer = bufferObject->buffer.getVkBuffer(),
|
||||
.buffer = bufferObject->getVkBuffer(),
|
||||
.offset = offset,
|
||||
.range = size,
|
||||
};
|
||||
|
||||
@@ -532,8 +532,8 @@ void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType ele
|
||||
uint32_t indexCount, BufferUsage usage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto elementSize = (uint8_t) getElementTypeSize(elementType);
|
||||
auto ib = resource_ptr<VulkanIndexBuffer>::make(&mResourceManager, ibh, mAllocator, mStagePool,
|
||||
mBufferCache, elementSize, indexCount);
|
||||
auto ib = resource_ptr<VulkanIndexBuffer>::make(&mResourceManager, ibh, mContext, mAllocator,
|
||||
mStagePool, mBufferCache, elementSize, indexCount);
|
||||
ib.inc();
|
||||
}
|
||||
|
||||
@@ -549,8 +549,8 @@ void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
|
||||
void VulkanDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto bo = resource_ptr<VulkanBufferObject>::make(&mResourceManager, boh, mAllocator, mStagePool,
|
||||
mBufferCache, byteCount, bindingType);
|
||||
auto bo = resource_ptr<VulkanBufferObject>::make(&mResourceManager, boh, mContext, mAllocator,
|
||||
mStagePool, mBufferCache, byteCount, bindingType);
|
||||
bo.inc();
|
||||
}
|
||||
|
||||
@@ -1239,7 +1239,7 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
|
||||
commands.acquire(ib);
|
||||
ib->buffer.loadFromCpu(commands, p.buffer, byteOffset, p.size);
|
||||
ib->loadFromCpu(commands, p.buffer, byteOffset, p.size);
|
||||
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
@@ -1255,7 +1255,7 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript
|
||||
|
||||
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
|
||||
commands.acquire(bo);
|
||||
bo->buffer.loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
||||
bo->loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
||||
|
||||
scheduleDestroy(std::move(bd));
|
||||
}
|
||||
@@ -1266,7 +1266,7 @@ void VulkanDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> boh,
|
||||
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
|
||||
commands.acquire(bo);
|
||||
// TODO: implement unsynchronized version
|
||||
bo->buffer.loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
||||
bo->loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
||||
scheduleDestroy(std::move(bd));
|
||||
}
|
||||
|
||||
@@ -1915,7 +1915,7 @@ void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
// avoid rebinding these if they are already bound, but since we do not (yet) support subranges
|
||||
// it would be rare for a client to make consecutive draw calls with the same render primitive.
|
||||
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
|
||||
vkCmdBindIndexBuffer(cmdbuffer, prim->indexBuffer->buffer.getVkBuffer(), 0,
|
||||
vkCmdBindIndexBuffer(cmdbuffer, prim->indexBuffer->getVkBuffer(), 0,
|
||||
prim->indexBuffer->indexType);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,20 @@ namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
inline VulkanBufferUsage getBufferObjectUsage(BufferObjectBinding bindingType) noexcept {
|
||||
switch (bindingType) {
|
||||
case BufferObjectBinding::VERTEX:
|
||||
return VulkanBufferUsage::VERTEX;
|
||||
case BufferObjectBinding::UNIFORM:
|
||||
return VulkanBufferUsage::UNIFORM;
|
||||
case BufferObjectBinding::SHADER_STORAGE:
|
||||
return VulkanBufferUsage::SHADER_STORAGE;
|
||||
// when adding more buffer-types here, make sure to update VulkanBuffer::loadFromCpu()
|
||||
// if necessary.
|
||||
}
|
||||
return VulkanBufferUsage::UNKNOWN;
|
||||
}
|
||||
|
||||
void flipVertically(VkViewport* rect, uint32_t framebufferHeight) {
|
||||
rect->y = framebufferHeight - rect->y - rect->height;
|
||||
}
|
||||
@@ -166,14 +180,6 @@ VulkanAttachment createSwapchainAttachment(const fvkmemory::resource_ptr<VulkanT
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanTexture> texture) {
|
||||
mResources.push_back(texture);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanBufferObject> obj) {
|
||||
mResources.push_back(obj);
|
||||
}
|
||||
|
||||
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout,
|
||||
VkDescriptorSetLayout vkLayout)
|
||||
: bitmask(fromBackendLayout(layout)),
|
||||
@@ -185,6 +191,17 @@ VulkanDescriptorSetLayout::Bitmask VulkanDescriptorSetLayout::Bitmask::fromLayou
|
||||
return fromBackendLayout(layout);
|
||||
}
|
||||
|
||||
// This method will store an age associated with this command buffer into the VulkanBuffer, which
|
||||
// will allow us to determine whether a barrier is necessary or not.
|
||||
void VulkanDescriptorSet::referencedBy(VulkanCommandBuffer& commands) {
|
||||
mUboMask.forEachSetBit([this, &commands](size_t index) {
|
||||
auto& res = mResources[index];
|
||||
fvkmemory::resource_ptr<VulkanBufferObject> bo =
|
||||
fvkmemory::resource_ptr<VulkanBufferObject>::cast((VulkanBufferObject*) res.get());
|
||||
bo->referencedBy(commands);
|
||||
});
|
||||
}
|
||||
|
||||
PushConstantDescription::PushConstantDescription(backend::Program const& program) {
|
||||
mRangeCount = 0;
|
||||
for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
|
||||
@@ -588,17 +605,18 @@ void VulkanVertexBuffer::setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> b
|
||||
int8_t const* const attribToBuffer = vbi->getAttributeToBuffer();
|
||||
for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) {
|
||||
if (attribToBuffer[attribIndex] == static_cast<int8_t>(index)) {
|
||||
vkbuffers[attribIndex] = bufferObject->buffer.getVkBuffer();
|
||||
vkbuffers[attribIndex] = bufferObject->getVkBuffer();
|
||||
}
|
||||
}
|
||||
mResources.push_back(bufferObject);
|
||||
}
|
||||
|
||||
VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
VulkanBufferCache& bufferCache, uint32_t byteCount, BufferObjectBinding bindingType)
|
||||
VulkanBufferObject::VulkanBufferObject(VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType)
|
||||
: HwBufferObject(byteCount),
|
||||
buffer(allocator, stagePool, bufferCache, getBufferObjectUsage(bindingType), byteCount),
|
||||
bindingType(bindingType) {}
|
||||
bindingType(bindingType),
|
||||
mBuffer(context, allocator, stagePool, bufferCache, getBufferObjectUsage(bindingType), byteCount) {}
|
||||
|
||||
VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
|
||||
fvkmemory::resource_ptr<VulkanVertexBuffer> vb,
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "VulkanFboCache.h"
|
||||
#include "VulkanSwapChain.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/VulkanCommands.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
#include "vulkan/utils/Definitions.h"
|
||||
#include "vulkan/utils/StaticVector.h"
|
||||
@@ -200,8 +201,15 @@ public:
|
||||
return &mOffsets;
|
||||
}
|
||||
|
||||
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
|
||||
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
|
||||
template<typename Resource>
|
||||
void acquire(fvkmemory::resource_ptr<Resource> res) {
|
||||
if (res->template isType<VulkanBufferObject>()) {
|
||||
mUboMask.set(mResources.size());
|
||||
}
|
||||
mResources.push_back(res);
|
||||
}
|
||||
|
||||
void referencedBy(VulkanCommandBuffer& commands);
|
||||
|
||||
fvkutils::UniformBufferBitmask const dynamicUboMask;
|
||||
uint8_t const uniqueDynamicUboCount;
|
||||
@@ -214,6 +222,7 @@ private:
|
||||
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
|
||||
OnRecycle mOnRecycleFn;
|
||||
OnRecycle mOnRecycleExternalSamplerFn;
|
||||
fvkutils::UniformBufferBitmask mUboMask;
|
||||
};
|
||||
|
||||
using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
|
||||
@@ -419,6 +428,9 @@ struct VulkanVertexBuffer : public HwVertexBuffer, fvkmemory::Resource {
|
||||
fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi);
|
||||
void setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, uint32_t index);
|
||||
|
||||
// TODO: because VulkanBufferObject is backed by VulkanBufferProxy, which could switch out the
|
||||
// backing VkBuffer, we cannot store the VkBuffers for optimization here. We could store a dirty
|
||||
// bit to indicate if a VkBuffer has changed maybe.
|
||||
inline VkBuffer const* getVkBuffers() const { return mBuffers.data(); }
|
||||
inline VkBuffer* getVkBuffers() { return mBuffers.data(); }
|
||||
fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi;
|
||||
@@ -429,23 +441,47 @@ private:
|
||||
};
|
||||
|
||||
struct VulkanIndexBuffer : public HwIndexBuffer, fvkmemory::Resource {
|
||||
VulkanIndexBuffer(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
VulkanBufferCache& bufferCache, uint8_t elementSize, uint32_t indexCount)
|
||||
VulkanIndexBuffer(VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, uint8_t elementSize,
|
||||
uint32_t indexCount)
|
||||
: HwIndexBuffer(elementSize, indexCount),
|
||||
buffer(allocator, stagePool, bufferCache, VulkanBufferUsage::INDEX,
|
||||
elementSize * indexCount),
|
||||
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32) {}
|
||||
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32),
|
||||
mBuffer(context, allocator, stagePool, bufferCache, VulkanBufferUsage::INDEX,
|
||||
elementSize * indexCount) {}
|
||||
|
||||
VulkanBufferProxy buffer;
|
||||
const VkIndexType indexType;
|
||||
inline void loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData, uint32_t byteOffset,
|
||||
uint32_t numBytes) {
|
||||
mBuffer.loadFromCpu(commands, cpuData, byteOffset, numBytes);
|
||||
}
|
||||
|
||||
inline VkBuffer getVkBuffer() const noexcept { return mBuffer.getVkBuffer(); }
|
||||
|
||||
VkIndexType const indexType;
|
||||
|
||||
private:
|
||||
VulkanBufferProxy mBuffer;
|
||||
};
|
||||
|
||||
struct VulkanBufferObject : public HwBufferObject, fvkmemory::Resource {
|
||||
VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
VulkanBufferCache& bufferCache, uint32_t byteCount, BufferObjectBinding bindingType);
|
||||
VulkanBufferObject(VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType);
|
||||
|
||||
VulkanBufferProxy buffer;
|
||||
const BufferObjectBinding bindingType;
|
||||
inline void loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData, uint32_t byteOffset,
|
||||
uint32_t numBytes) {
|
||||
mBuffer.loadFromCpu(commands, cpuData, byteOffset, numBytes);
|
||||
}
|
||||
|
||||
inline VkBuffer getVkBuffer() const noexcept { return mBuffer.getVkBuffer(); }
|
||||
|
||||
inline void referencedBy(VulkanCommandBuffer& commands) {
|
||||
mBuffer.referencedBy(commands);
|
||||
}
|
||||
|
||||
BufferObjectBinding const bindingType;
|
||||
|
||||
private:
|
||||
VulkanBufferProxy mBuffer;
|
||||
};
|
||||
|
||||
struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
|
||||
@@ -457,21 +493,6 @@ struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
|
||||
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer;
|
||||
};
|
||||
|
||||
inline constexpr VulkanBufferUsage getBufferObjectUsage(BufferObjectBinding bindingType) noexcept {
|
||||
switch (bindingType) {
|
||||
case BufferObjectBinding::VERTEX:
|
||||
return VulkanBufferUsage::VERTEX;
|
||||
case BufferObjectBinding::UNIFORM:
|
||||
return VulkanBufferUsage::UNIFORM;
|
||||
case BufferObjectBinding::SHADER_STORAGE:
|
||||
return VulkanBufferUsage::SHADER_STORAGE;
|
||||
// when adding more buffer-types here, make sure to update VulkanBuffer::loadFromCpu()
|
||||
// if necessary.
|
||||
}
|
||||
|
||||
return VulkanBufferUsage::UNKNOWN;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANHANDLES_H
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
|
||||
#include "utils/Definitions.h"
|
||||
#include "vulkan/utils/Definitions.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
|
||||
@@ -72,7 +72,10 @@ struct Resource {
|
||||
restype(ResourceType::UNDEFINED_TYPE),
|
||||
mHandleConsideredDestroyed(false) {}
|
||||
|
||||
uint32_t getCount() const { return mCount; }
|
||||
template<typename D>
|
||||
bool isType() const {
|
||||
return getTypeEnum<D>() == restype;
|
||||
}
|
||||
|
||||
private:
|
||||
inline void inc() noexcept {
|
||||
|
||||
@@ -741,6 +741,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
}
|
||||
|
||||
VulkanContext& context = mImpl->mContext;
|
||||
|
||||
// Pass along relevant driver config (feature flags)
|
||||
context.mStagingBufferBypassEnabled = driverConfig.vulkanEnableStagingBufferBypass;
|
||||
|
||||
ExtensionSet instExts;
|
||||
// If using a shared context, we do not assume any extensions.
|
||||
if (!mImpl->mSharedContext) {
|
||||
|
||||
@@ -410,7 +410,7 @@ void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
|
||||
Handle<HwTexture> sourceTextureHandle, const backend::TextureSwizzle r,
|
||||
const backend::TextureSwizzle g, const backend::TextureSwizzle b,
|
||||
const backend::TextureSwizzle a) {
|
||||
PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
|
||||
// PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
|
||||
@@ -964,15 +964,15 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
!(hasStencil(customDepthStencilFormat))) {
|
||||
FILAMENT_CHECK_POSTCONDITION(false)
|
||||
<< "Custom render target requested stencil, but the provided texture"
|
||||
"format number"
|
||||
<< (uint32_t) customDepthStencilFormat
|
||||
"format number "
|
||||
<< webGPUTextureFormatToString(customDepthStencilFormat)
|
||||
<< " does not have a stencil aspect.";
|
||||
}
|
||||
if (any(renderTarget->getTargetFlags() & TargetBufferFlags::DEPTH) &&
|
||||
!(hasDepth(customDepthStencilFormat))) {
|
||||
FILAMENT_CHECK_POSTCONDITION(false) << "Custom render target requested depth, "
|
||||
"but the provided texture format number"
|
||||
<< (uint32_t) customDepthStencilFormat
|
||||
<< webGPUTextureFormatToString(customDepthStencilFormat)
|
||||
<< " does not have a depth aspect.";
|
||||
}
|
||||
}
|
||||
@@ -1079,7 +1079,7 @@ void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
|
||||
mCommandBuffer = nullptr;
|
||||
mTextureView = nullptr;
|
||||
assert_invariant(mSwapChain);
|
||||
mSwapChain->present(mQueue);
|
||||
mSwapChain->present();
|
||||
}
|
||||
|
||||
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
@@ -1110,84 +1110,8 @@ void WebGPUDriver::stopCapture(int) {
|
||||
void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, const uint32_t x,
|
||||
const uint32_t y, const uint32_t width, const uint32_t height,
|
||||
PixelBufferDescriptor&& pixelBufferDescriptor) {
|
||||
auto srcTarget = handleCast<WebGPURenderTarget>(sourceRenderTargetHandle);
|
||||
assert_invariant(srcTarget);
|
||||
|
||||
wgpu::Texture srcTexture = nullptr;
|
||||
if (srcTarget->isDefaultRenderTarget()) {
|
||||
assert_invariant(mSwapChain);
|
||||
srcTexture = mSwapChain->getCurrentTexture(mPlatform.getSurfaceExtent(mNativeWindow));
|
||||
} else {
|
||||
// TODO: Handle custom render targets
|
||||
scheduleDestroy(std::move(pixelBufferDescriptor));
|
||||
return;
|
||||
}
|
||||
assert_invariant(srcTexture);
|
||||
|
||||
// Create a staging buffer to copy the texture to
|
||||
const size_t bytesPerPixel = 4;
|
||||
const size_t unpaddedBytesPerRow = width * bytesPerPixel;
|
||||
const size_t alignment = 256;
|
||||
const size_t paddedBytesPerRow = (unpaddedBytesPerRow + alignment - 1) & ~(alignment - 1);
|
||||
|
||||
size_t bufferSize = paddedBytesPerRow * height;
|
||||
wgpu::BufferDescriptor bufferDesc;
|
||||
bufferDesc.size = bufferSize;
|
||||
bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
|
||||
wgpu::Buffer stagingBuffer = mDevice.CreateBuffer(&bufferDesc);
|
||||
assert_invariant(stagingBuffer);
|
||||
|
||||
wgpu::CommandEncoder encoder = mDevice.CreateCommandEncoder();
|
||||
|
||||
wgpu::TexelCopyTextureInfo source {
|
||||
.texture = srcTexture,
|
||||
.mipLevel = 0,
|
||||
.origin = { x, y, 0 }
|
||||
};
|
||||
wgpu::TexelCopyBufferInfo destination {
|
||||
.layout = {
|
||||
.offset = 0,
|
||||
.bytesPerRow = static_cast<uint32_t>(paddedBytesPerRow),
|
||||
.rowsPerImage = height,
|
||||
},
|
||||
.buffer = stagingBuffer
|
||||
};
|
||||
wgpu::Extent3D copySize {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.depthOrArrayLayers = 1
|
||||
};
|
||||
encoder.CopyTextureToBuffer(&source, &destination, ©Size);
|
||||
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
||||
mQueue.Submit(1, &commandBuffer);
|
||||
|
||||
// Map the buffer to read the data
|
||||
struct UserData {
|
||||
PixelBufferDescriptor pbd;
|
||||
wgpu::Buffer buffer;
|
||||
size_t unpaddedBytesPerRow;
|
||||
size_t paddedBytesPerRow;
|
||||
uint32_t height;
|
||||
};
|
||||
auto userData = std::make_unique<UserData>();
|
||||
userData->pbd = std::move(pixelBufferDescriptor);
|
||||
userData->buffer = stagingBuffer;
|
||||
userData->unpaddedBytesPerRow = unpaddedBytesPerRow;
|
||||
userData->paddedBytesPerRow = paddedBytesPerRow;
|
||||
userData->height = height;
|
||||
|
||||
stagingBuffer.MapAsync(wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowSpontaneous, [](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
|
||||
std::unique_ptr<UserData> data(static_cast<UserData*>(userdata));
|
||||
if (status == wgpu::MapAsyncStatus::Success) {
|
||||
const char* src = static_cast<const char*>(data->buffer.GetConstMappedRange(0, data->buffer.GetSize()));
|
||||
char* dst = static_cast<char*>(data->pbd.buffer);
|
||||
for (uint32_t i = 0; i < data->height; ++i) {
|
||||
memcpy(dst + i * data->unpaddedBytesPerRow, src + i * data->paddedBytesPerRow, data->unpaddedBytesPerRow);
|
||||
}
|
||||
data->buffer.Unmap();
|
||||
}
|
||||
// scheduleDestroy(std::move(data->pbd)); // This line is problematic, need to pass scheduleDestroy func
|
||||
}, userData.release());
|
||||
// todo
|
||||
scheduleDestroy(std::move(pixelBufferDescriptor));
|
||||
}
|
||||
|
||||
void WebGPUDriver::readBufferSubData(Handle<HwBufferObject> bufferObjectHandle,
|
||||
|
||||
@@ -377,24 +377,8 @@ wgpu::TextureView WebGPUSwapChain::getCurrentHeadlessTextureView() {
|
||||
return mRenderTargetViews[mHeadlessBufferIndex];
|
||||
}
|
||||
|
||||
wgpu::Texture WebGPUSwapChain::getCurrentTexture(wgpu::Extent2D const& extent) {
|
||||
void WebGPUSwapChain::present() {
|
||||
if (isHeadless()) {
|
||||
return mRenderTargetTextures[mHeadlessBufferIndex];
|
||||
} else {
|
||||
setExtent(extent);
|
||||
wgpu::SurfaceTexture surfaceTexture;
|
||||
mSurface.GetCurrentTexture(&surfaceTexture);
|
||||
return surfaceTexture.texture;
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPUSwapChain::present(wgpu::Queue const& queue) {
|
||||
if (isHeadless()) {
|
||||
// To ensure the CPU doesn't read the texture before the GPU is done,
|
||||
// we wait for the queue to be idle.
|
||||
wgpu::Future future = queue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus) {});
|
||||
wgpu::FutureWaitInfo waitInfo { .future = future };
|
||||
mDevice.GetAdapter().GetInstance().WaitAny(1, &waitInfo, UINT64_MAX);
|
||||
mHeadlessBufferIndex = (mHeadlessBufferIndex + 1) % mHeadlessBufferCount;
|
||||
} else {
|
||||
mSurface.Present();
|
||||
|
||||
@@ -42,13 +42,12 @@ public:
|
||||
|
||||
[[nodiscard]] wgpu::TextureView getCurrentTextureView(wgpu::Extent2D const& extent);
|
||||
[[nodiscard]] wgpu::TextureView getCurrentHeadlessTextureView();
|
||||
[[nodiscard]] wgpu::Texture getCurrentTexture(wgpu::Extent2D const& extent);
|
||||
|
||||
[[nodiscard]] wgpu::TextureView getDepthTextureView() const { return mDepthTextureView; }
|
||||
|
||||
[[nodiscard]] bool isHeadless() const { return mType == SwapChainType::HEADLESS; }
|
||||
|
||||
void present(wgpu::Queue const& queue);
|
||||
void present();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace {
|
||||
case wgpu::TextureFormat::BGRA8UnormSrgb: return wgpu::TextureFormat::BGRA8Unorm;
|
||||
case wgpu::TextureFormat::BC1RGBAUnormSrgb: return wgpu::TextureFormat::BC1RGBAUnorm;
|
||||
case wgpu::TextureFormat::BC2RGBAUnormSrgb: return wgpu::TextureFormat::BC2RGBAUnorm;
|
||||
case wgpu::TextureFormat::BC3RGBAUnormSrgb: return wgpu::TextureFormat::BC3RGBAUnorm;
|
||||
case wgpu::TextureFormat::BC3RGBAUnormSrgb: return wgpu::TextureFormat::BC3RGBAUnorm;
|
||||
case wgpu::TextureFormat::BC7RGBAUnormSrgb: return wgpu::TextureFormat::BC7RGBAUnorm;
|
||||
case wgpu::TextureFormat::ETC2RGB8UnormSrgb: return wgpu::TextureFormat::ETC2RGB8Unorm;
|
||||
case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb: return wgpu::TextureFormat::ETC2RGB8A1Unorm;
|
||||
@@ -329,12 +329,12 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
|
||||
const uint32_t height, const uint32_t depth, const TextureUsage usage,
|
||||
wgpu::Device const& device) noexcept
|
||||
: HwTexture{ samplerType, levels, samples, width, height, depth, format, usage },
|
||||
mViewFormat{ fToWGPUTextureFormat(format) },
|
||||
mViewFormat{ fToWGPUTextureFormat(format, usage) },
|
||||
mMipmapGenerationStrategy{ determineMipmapGenerationStrategy(mViewFormat, samplerType,
|
||||
samples, levels) },
|
||||
mWebGPUFormat{ mMipmapGenerationStrategy == MipmapGenerationStrategy::SPD_COMPUTE_PASS
|
||||
? storageBindingCompatibleFormatForViewFormat(mViewFormat)
|
||||
: mViewFormat },
|
||||
: fToWGPUTextureFormat(format, usage) },
|
||||
mAspect{ fToWGPUTextureViewAspect(usage, format) },
|
||||
mWebGPUUsage{ fToWGPUTextureUsage(usage, samples,
|
||||
mMipmapGenerationStrategy == MipmapGenerationStrategy::SPD_COMPUTE_PASS,
|
||||
@@ -495,6 +495,66 @@ wgpu::TextureView WebGPUTexture::makeMsaaSidecarTextureViewIfTextureSidecarExist
|
||||
return textureView;
|
||||
}
|
||||
|
||||
wgpu::TextureFormat WebGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFormat,
|
||||
TextureUsage const& fUsage) {
|
||||
|
||||
const bool isDepth{ any(fUsage & TextureUsage::DEPTH_ATTACHMENT) };
|
||||
const bool isStencil{ any(fUsage & TextureUsage::STENCIL_ATTACHMENT) };
|
||||
const bool isColor{ any(fUsage & TextureUsage::COLOR_ATTACHMENT) };
|
||||
const bool isBlitSrc{ any(fUsage & TextureUsage::BLIT_SRC) };
|
||||
const bool isBlitDst{ any(fUsage & TextureUsage::BLIT_DST) };
|
||||
const bool isUploadable{ any(fUsage & TextureUsage::UPLOADABLE) };
|
||||
const bool isSampleable{ any(fUsage & TextureUsage::SAMPLEABLE) };
|
||||
const bool isSubpassInput{ any(fUsage & TextureUsage::SUBPASS_INPUT) };
|
||||
const bool isProtected{ any(fUsage & TextureUsage::PROTECTED) };
|
||||
|
||||
FWGPU_LOGD << ""
|
||||
<< " isDepth: "
|
||||
<< isDepth
|
||||
<< " isStencil: "
|
||||
<< isStencil
|
||||
<< " isColor: "
|
||||
<< isColor
|
||||
<< " isBlitSrc: "
|
||||
<< isBlitSrc
|
||||
<< " isBlitDst: "
|
||||
<< isBlitDst
|
||||
<< " isUploadable: "
|
||||
<< isUploadable
|
||||
<< " isSampleable: "
|
||||
<< isSampleable
|
||||
<< " isSubpassInput: "
|
||||
<< isSubpassInput
|
||||
<< " isProtected: "
|
||||
<< isProtected;
|
||||
|
||||
const bool depthOnly{ isDepth && !isColor && !isStencil };
|
||||
const bool stencilOnly = { isStencil && !isColor && !isDepth };
|
||||
if (depthOnly || stencilOnly) {
|
||||
switch (fFormat) {
|
||||
case TextureFormat::DEPTH24_STENCIL8:
|
||||
if (depthOnly && stencilOnly) {
|
||||
return wgpu::TextureFormat::Depth24PlusStencil8;
|
||||
} else if (depthOnly) {
|
||||
return wgpu::TextureFormat::Depth24Plus;
|
||||
} else {
|
||||
return wgpu::TextureFormat::Stencil8;
|
||||
}
|
||||
case TextureFormat::DEPTH32F_STENCIL8:
|
||||
if (depthOnly && stencilOnly) {
|
||||
return wgpu::TextureFormat::Depth32FloatStencil8;
|
||||
} else if (depthOnly) {
|
||||
return wgpu::TextureFormat::Depth32Float;
|
||||
} else {
|
||||
return wgpu::TextureFormat::Stencil8;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fToWGPUTextureFormat(fFormat);
|
||||
}
|
||||
|
||||
wgpu::TextureFormat WebGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFormat) {
|
||||
switch (fFormat) {
|
||||
case TextureFormat::R8: return wgpu::TextureFormat::R8Unorm;
|
||||
|
||||
@@ -104,10 +104,13 @@ public:
|
||||
/**
|
||||
* @return nullptr if a MSAA sidecar texture is not appliable, otherwise a view to one
|
||||
*/
|
||||
[[nodiscard]] wgpu::TextureView makeMsaaSidecarTextureView(wgpu::Texture const&, uint8_t mipLevel, uint32_t arrayLayer) const;
|
||||
[[nodiscard]] wgpu::TextureView makeMsaaSidecarTextureView(wgpu::Texture const&,
|
||||
uint8_t mipLevel, uint32_t arrayLayer) const;
|
||||
|
||||
[[nodiscard]] static wgpu::TextureFormat fToWGPUTextureFormat(
|
||||
filament::backend::TextureFormat const& fFormat);
|
||||
[[nodiscard]] static wgpu::TextureFormat fToWGPUTextureFormat(
|
||||
filament::backend::TextureFormat const& fFormat, TextureUsage const& fUsage);
|
||||
|
||||
/**
|
||||
* @param format a required texture format (can be a view to a different underlying texture
|
||||
|
||||
@@ -329,6 +329,7 @@ void PostProcessManager::init() noexcept {
|
||||
|
||||
mSsrPassDescriptorSet.init(engine);
|
||||
mPostProcessDescriptorSet.init(engine);
|
||||
mStructureDescriptorSet.init(engine);
|
||||
|
||||
mWorkaroundSplitEasu =
|
||||
driver.isWorkaroundNeeded(Workaround::SPLIT_EASU);
|
||||
@@ -405,6 +406,7 @@ void PostProcessManager::terminate(DriverApi& driver) noexcept {
|
||||
|
||||
mPostProcessDescriptorSet.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
mSsrPassDescriptorSet.terminate(driver);
|
||||
mStructureDescriptorSet.terminate(driver);
|
||||
}
|
||||
|
||||
Handle<HwTexture> PostProcessManager::getOneTexture() const {
|
||||
@@ -546,7 +548,8 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
Variant structureVariant(Variant::DEPTH_VARIANT);
|
||||
structureVariant.setPicking(config.picking);
|
||||
|
||||
bindPostProcessDescriptorSet(driver);
|
||||
// bind the per-view descriptorSet that is used for the structure pass
|
||||
getStructureDescriptorSet().bind(driver);
|
||||
|
||||
passBuilder.renderFlags(structureRenderFlags);
|
||||
passBuilder.variant(structureVariant);
|
||||
@@ -609,6 +612,63 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
return { depth, structurePass->picking };
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::transparentPicking(FrameGraph& fg,
|
||||
RenderPassBuilder const& passBuilder, uint8_t const structureRenderFlags,
|
||||
uint32_t width, uint32_t height, float const scale) noexcept {
|
||||
|
||||
struct PickingRenderPassData {
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
FrameGraphId<FrameGraphTexture> picking;
|
||||
};
|
||||
auto const& pickingRenderPass = fg.addPass<PickingRenderPassData>("Picking Render Pass",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
bool const isFL0 = mEngine.getDriverApi().getFeatureLevel() ==
|
||||
FeatureLevel::FEATURE_LEVEL_0;
|
||||
|
||||
// TODO: Specify the precision for picking pass
|
||||
width = std::max(32u, uint32_t(std::ceil(float(width) * scale)));
|
||||
height = std::max(32u, uint32_t(std::ceil(float(height) * scale)));
|
||||
data.depth = builder.createTexture("Depth Buffer", {
|
||||
.width = width, .height = height,
|
||||
.format = isFL0 ? TextureFormat::DEPTH24 : TextureFormat::DEPTH32F });
|
||||
|
||||
data.depth = builder.write(data.depth,
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
data.picking = builder.createTexture("Picking Buffer", {
|
||||
.width = width, .height = height,
|
||||
.format = isFL0 ? TextureFormat::RGBA8 : TextureFormat::RG32F });
|
||||
|
||||
data.picking = builder.write(data.picking,
|
||||
FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
|
||||
builder.declareRenderPass("Picking Render Target", {
|
||||
.attachments = {.color = { data.picking }, .depth = data.depth },
|
||||
.clearFlags = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH
|
||||
});
|
||||
},
|
||||
[=, passBuilder = passBuilder](FrameGraphResources const& resources,
|
||||
auto const&, DriverApi& driver) mutable {
|
||||
Variant pickingVariant(Variant::DEPTH_VARIANT);
|
||||
pickingVariant.setPicking(true);
|
||||
|
||||
// bind the per-view descriptorSet that is used for the structure pass
|
||||
getStructureDescriptorSet().bind(driver);
|
||||
|
||||
auto [target, params] = resources.getRenderPassInfo();
|
||||
passBuilder.renderFlags(structureRenderFlags);
|
||||
passBuilder.variant(pickingVariant);
|
||||
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::DEPTH);
|
||||
|
||||
RenderPass const pass{ passBuilder.build(mEngine, driver) };
|
||||
driver.beginRenderPass(target, params);
|
||||
pass.getExecutor().execute(mEngine, driver);
|
||||
driver.endRenderPass();
|
||||
});
|
||||
|
||||
return pickingRenderPass->picking;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
|
||||
@@ -858,7 +918,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
});
|
||||
},
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
bindPostProcessDescriptorSet(driver);
|
||||
// bind the per-view descriptorSet that is used for the structure pass
|
||||
getStructureDescriptorSet().bind(driver);
|
||||
|
||||
auto depth = resources.getTexture(data.depth);
|
||||
auto ssao = resources.getRenderPassInfo();
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "ds/PostProcessDescriptorSet.h"
|
||||
#include "ds/SsrPassDescriptorSet.h"
|
||||
#include "ds/StructureDescriptorSet.h"
|
||||
#include "ds/TypedUniformBuffer.h"
|
||||
|
||||
#include "materials/StaticMaterialInfo.h"
|
||||
@@ -104,6 +105,10 @@ public:
|
||||
RenderPassBuilder const& passBuilder, uint8_t structureRenderFlags,
|
||||
uint32_t width, uint32_t height, StructurePassConfig const& config) noexcept;
|
||||
|
||||
FrameGraphId<FrameGraphTexture> transparentPicking(FrameGraph& fg,
|
||||
RenderPassBuilder const& passBuilder, uint8_t structureRenderFlags,
|
||||
uint32_t width, uint32_t height, float scale) noexcept;
|
||||
|
||||
// reflections pass
|
||||
FrameGraphId<FrameGraphTexture> ssr(FrameGraph& fg,
|
||||
RenderPassBuilder const& passBuilder,
|
||||
@@ -391,7 +396,9 @@ public:
|
||||
FMaterialInstance* configureColorGradingMaterial(
|
||||
PostProcessMaterial& material, FColorGrading const* colorGrading,
|
||||
ColorGradingConfig const& colorGradingConfig, VignetteOptions const& vignetteOptions,
|
||||
uint32_t const width, uint32_t const height) noexcept;
|
||||
uint32_t width, uint32_t height) noexcept;
|
||||
|
||||
StructureDescriptorSet& getStructureDescriptorSet() const noexcept { return mStructureDescriptorSet; }
|
||||
|
||||
private:
|
||||
backend::RenderPrimitiveHandle mFullScreenQuadRph;
|
||||
@@ -402,6 +409,7 @@ private:
|
||||
|
||||
mutable SsrPassDescriptorSet mSsrPassDescriptorSet;
|
||||
mutable PostProcessDescriptorSet mPostProcessDescriptorSet;
|
||||
mutable StructureDescriptorSet mStructureDescriptorSet;
|
||||
|
||||
struct BilateralPassConfig {
|
||||
uint8_t kernelSize = 11;
|
||||
|
||||
@@ -521,9 +521,9 @@ public:
|
||||
}
|
||||
|
||||
// Specifies camera information (e.g. used for sorting commands)
|
||||
RenderPassBuilder& camera(const CameraInfo& camera) noexcept {
|
||||
mCameraPosition = camera.getPosition();
|
||||
mCameraForwardVector = camera.getForwardVector();
|
||||
RenderPassBuilder& camera(math::float3 position, math::float3 forward) noexcept {
|
||||
mCameraPosition = position;
|
||||
mCameraForwardVector = forward;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverAp
|
||||
|
||||
mMaterialInstance = downcast(entry.materialInstance);
|
||||
mBlendOrder = entry.blendOrder;
|
||||
mGlobalBlendOrderEnabled = entry.globalBlendOrderEnabled;
|
||||
|
||||
if (entry.indices && entry.vertices) {
|
||||
FVertexBuffer const* vertexBuffer = downcast(entry.vertices);
|
||||
|
||||
@@ -1366,8 +1366,8 @@ float4 ShadowMap::getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void ShadowMap::prepareCamera(Transaction const& transaction,
|
||||
DriverApi& driver, const CameraInfo& cameraInfo) noexcept {
|
||||
ShadowMapDescriptorSet::prepareCamera(transaction, driver, cameraInfo);
|
||||
FEngine const& engine, const CameraInfo& cameraInfo) noexcept {
|
||||
ShadowMapDescriptorSet::prepareCamera(transaction, engine, cameraInfo);
|
||||
ShadowMapDescriptorSet::prepareLodBias(transaction, 0.0f);
|
||||
}
|
||||
|
||||
@@ -1381,6 +1381,11 @@ void ShadowMap::prepareTime(Transaction const& transaction,
|
||||
ShadowMapDescriptorSet::prepareTime(transaction, engine, userTime);
|
||||
}
|
||||
|
||||
void ShadowMap::prepareMaterialGlobals(Transaction const& transaction,
|
||||
std::array<float4, 4> const& materialGlobals) noexcept {
|
||||
ShadowMapDescriptorSet::prepareMaterialGlobals(transaction, materialGlobals);
|
||||
}
|
||||
|
||||
void ShadowMap::prepareShadowMapping(Transaction const& transaction,
|
||||
bool const highPrecision) noexcept {
|
||||
ShadowMapDescriptorSet::prepareShadowMapping(transaction, highPrecision);
|
||||
|
||||
@@ -33,9 +33,6 @@
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
@@ -194,11 +191,13 @@ public:
|
||||
using Transaction = ShadowMapDescriptorSet::Transaction;
|
||||
|
||||
static void prepareCamera(Transaction const& transaction,
|
||||
backend::DriverApi& driver, const CameraInfo& cameraInfo) noexcept;
|
||||
FEngine const& engine, const CameraInfo& cameraInfo) noexcept;
|
||||
static void prepareViewport(Transaction const& transaction,
|
||||
backend::Viewport const& viewport) noexcept;
|
||||
static void prepareTime(Transaction const& transaction,
|
||||
FEngine const& engine, math::float4 const& userTime) noexcept;
|
||||
static void prepareMaterialGlobals(Transaction const& transaction,
|
||||
std::array<math::float4, 4> const& materialGlobals) noexcept;
|
||||
static void prepareShadowMapping(Transaction const& transaction,
|
||||
bool highPrecision) noexcept;
|
||||
static ShadowMapDescriptorSet::Transaction open(backend::DriverApi& driver) noexcept;
|
||||
@@ -326,12 +325,12 @@ private:
|
||||
static float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
|
||||
uint16_t shadowDimension) noexcept;
|
||||
|
||||
static constexpr const Segment sBoxSegments[12] = {
|
||||
static constexpr Segment sBoxSegments[12] = {
|
||||
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
|
||||
{ 4, 5 }, { 5, 7 }, { 7, 6 }, { 6, 4 },
|
||||
{ 0, 4 }, { 1, 5 }, { 3, 7 }, { 2, 6 },
|
||||
};
|
||||
static constexpr const Quad sBoxQuads[6] = {
|
||||
static constexpr Quad sBoxQuads[6] = {
|
||||
{ 2, 0, 1, 3 }, // far
|
||||
{ 6, 4, 5, 7 }, // near
|
||||
{ 2, 0, 4, 6 }, // left
|
||||
|
||||
@@ -385,9 +385,10 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
const CameraInfo cameraInfo{ shadowMap.getCamera(), mainCameraInfo };
|
||||
|
||||
auto transaction = ShadowMap::open(driver);
|
||||
ShadowMap::prepareCamera(transaction, driver, cameraInfo);
|
||||
ShadowMap::prepareCamera(transaction, engine, cameraInfo);
|
||||
ShadowMap::prepareViewport(transaction, shadowMap.getViewport());
|
||||
ShadowMap::prepareTime(transaction, engine, userTime);
|
||||
ShadowMap::prepareMaterialGlobals(transaction, view.getMaterialGlobals());
|
||||
ShadowMap::prepareShadowMapping(transaction,
|
||||
vsmShadowOptions.highPrecision);
|
||||
shadowMap.commit(transaction, engine, driver);
|
||||
@@ -412,7 +413,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
|
||||
RenderPass const pass = passBuilder
|
||||
.renderFlags(RenderPass::HAS_DEPTH_CLAMP, renderPassFlags)
|
||||
.camera(cameraInfo)
|
||||
.camera(cameraInfo.getPosition(), cameraInfo.getForwardVector())
|
||||
.visibilityMask(entry.visibilityMask)
|
||||
.geometry(scene->getRenderableData(), entry.range)
|
||||
.commandTypeFlags(RenderPass::CommandTypeFlags::SHADOW)
|
||||
|
||||
@@ -94,6 +94,30 @@ namespace filament {
|
||||
using namespace backend;
|
||||
using namespace filaflat;
|
||||
|
||||
namespace {
|
||||
|
||||
backend::Platform::DriverConfig getDriverConfig(FEngine* instance) {
|
||||
return {
|
||||
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
|
||||
.metalUploadBufferSizeBytes = instance->getConfig().metalUploadBufferSizeBytes,
|
||||
.disableParallelShaderCompile = instance->features.backend.disable_parallel_shader_compile,
|
||||
.disableHandleUseAfterFreeCheck =
|
||||
instance->features.backend.disable_handle_use_after_free_check,
|
||||
.disableHeapHandleTags = instance->features.backend.disable_heap_handle_tags,
|
||||
.forceGLES2Context = instance->getConfig().forceGLES2Context,
|
||||
.stereoscopicType = instance->getConfig().stereoscopicType,
|
||||
.assertNativeWindowIsValid =
|
||||
instance->features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure =
|
||||
instance->getConfig().metalDisablePanicOnDrawableFailure,
|
||||
.gpuContextPriority = instance->getConfig().gpuContextPriority,
|
||||
.vulkanEnableStagingBufferBypass =
|
||||
instance->features.backend.vulkan.enable_staging_buffer_bypass,
|
||||
};
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
|
||||
struct Engine::BuilderDetails {
|
||||
Backend mBackend = Backend::DEFAULT;
|
||||
Platform* mPlatform = nullptr;
|
||||
@@ -131,19 +155,7 @@ Engine* FEngine::create(Builder const& builder) {
|
||||
delete instance;
|
||||
return nullptr;
|
||||
}
|
||||
DriverConfig const driverConfig{
|
||||
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
|
||||
.metalUploadBufferSizeBytes = instance->getConfig().metalUploadBufferSizeBytes,
|
||||
.disableParallelShaderCompile = instance->features.backend.disable_parallel_shader_compile,
|
||||
.disableHandleUseAfterFreeCheck = instance->features.backend.disable_handle_use_after_free_check,
|
||||
.disableHeapHandleTags = instance->features.backend.disable_heap_handle_tags,
|
||||
.forceGLES2Context = instance->getConfig().forceGLES2Context,
|
||||
.stereoscopicType = instance->getConfig().stereoscopicType,
|
||||
.assertNativeWindowIsValid = instance->features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = instance->getConfig().metalDisablePanicOnDrawableFailure,
|
||||
.gpuContextPriority = instance->getConfig().gpuContextPriority,
|
||||
};
|
||||
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
|
||||
instance->mDriver = platform->createDriver(sharedContext, getDriverConfig(instance));
|
||||
|
||||
} else {
|
||||
// start the driver thread
|
||||
@@ -752,19 +764,7 @@ int FEngine::loop() {
|
||||
JobSystem::setThreadName("FEngine::loop");
|
||||
JobSystem::setThreadPriority(JobSystem::Priority::DISPLAY);
|
||||
|
||||
DriverConfig const driverConfig {
|
||||
.handleArenaSize = getRequestedDriverHandleArenaSize(),
|
||||
.metalUploadBufferSizeBytes = mConfig.metalUploadBufferSizeBytes,
|
||||
.disableParallelShaderCompile = features.backend.disable_parallel_shader_compile,
|
||||
.disableHandleUseAfterFreeCheck = features.backend.disable_handle_use_after_free_check,
|
||||
.disableHeapHandleTags = features.backend.disable_heap_handle_tags,
|
||||
.forceGLES2Context = mConfig.forceGLES2Context,
|
||||
.stereoscopicType = mConfig.stereoscopicType,
|
||||
.assertNativeWindowIsValid = features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = mConfig.metalDisablePanicOnDrawableFailure,
|
||||
.gpuContextPriority = mConfig.gpuContextPriority,
|
||||
};
|
||||
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
|
||||
mDriver = mPlatform->createDriver(mSharedGLContext, getDriverConfig(this));
|
||||
|
||||
mDriverBarrier.latch();
|
||||
if (UTILS_UNLIKELY(!mDriver)) {
|
||||
|
||||
@@ -737,6 +737,12 @@ public:
|
||||
struct {
|
||||
bool assert_native_window_is_valid = false;
|
||||
} opengl;
|
||||
struct {
|
||||
// On Unified Memory Architecture device, it is possible to bypass using the staging
|
||||
// buffer. This is an experimental feature that still needs to be implemented fully
|
||||
// before it can be fully enabled.
|
||||
bool enable_staging_buffer_bypass = false;
|
||||
} vulkan;
|
||||
bool disable_parallel_shader_compile = false;
|
||||
bool disable_handle_use_after_free_check = false;
|
||||
bool disable_heap_handle_tags = true; // FIXME: this should be false
|
||||
@@ -762,18 +768,21 @@ public:
|
||||
{ "engine.shadows.use_shadow_atlas",
|
||||
"Uses an array of atlases to store shadow maps.",
|
||||
&features.engine.shadows.use_shadow_atlas, false },
|
||||
{ "features.engine.debug.assert_material_instance_in_use",
|
||||
{ "engine.debug.assert_material_instance_in_use",
|
||||
"Assert when a MaterialInstance is destroyed while it is in use by RenderableManager.",
|
||||
&features.engine.debug.assert_material_instance_in_use, false },
|
||||
{ "features.engine.debug.assert_destroy_material_before_material_instance",
|
||||
{ "engine.debug.assert_destroy_material_before_material_instance",
|
||||
"Assert when a Material is destroyed but its instances are still alive.",
|
||||
&features.engine.debug.assert_destroy_material_before_material_instance, false },
|
||||
{ "features.engine.debug.assert_vertex_buffer_count_exceeds_8",
|
||||
{ "engine.debug.assert_vertex_buffer_count_exceeds_8",
|
||||
"Assert when a client's number of buffers for a VertexBuffer exceeds 8.",
|
||||
&features.engine.debug.assert_vertex_buffer_count_exceeds_8, false },
|
||||
{ "features.engine.debug.assert_vertex_buffer_attribute_stride_mult_of_4",
|
||||
{ "engine.debug.assert_vertex_buffer_attribute_stride_mult_of_4",
|
||||
"Assert that the attribute stride of a vertex buffer is a multiple of 4.",
|
||||
&features.engine.debug.assert_vertex_buffer_attribute_stride_mult_of_4, false },
|
||||
{ "backend.vulkan.enable_staging_buffer_bypass",
|
||||
"vulkan: enable a staging bypass logic for unified memory architecture",
|
||||
&features.backend.vulkan.enable_staging_buffer_bypass, false },
|
||||
}};
|
||||
|
||||
utils::Slice<const FeatureFlag> getFeatureFlags() const noexcept {
|
||||
|
||||
@@ -619,10 +619,10 @@ Program FMaterial::getProgramWithVariants(
|
||||
.shader(ShaderStage::FRAGMENT, fsBuilder.data(), fsBuilder.size())
|
||||
.shaderLanguage(mMaterialParser->getShaderLanguage())
|
||||
.diagnostics(mName,
|
||||
[this, variant, vertexVariant, fragmentVariant](
|
||||
[variant, vertexVariant, fragmentVariant](utils::CString const& name,
|
||||
io::ostream& out) -> io::ostream& {
|
||||
return out << mName.c_str_safe() << ", variant=(" << io::hex
|
||||
<< +variant.key << io::dec << "), vertexVariant=(" << io::hex
|
||||
return out << name.c_str_safe() << ", variant=(" << io::hex << +variant.key
|
||||
<< io::dec << "), vertexVariant=(" << io::hex
|
||||
<< +vertexVariant.key << io::dec << "), fragmentVariant=("
|
||||
<< io::hex << +fragmentVariant.key << io::dec << ")";
|
||||
});
|
||||
|
||||
@@ -284,11 +284,13 @@ void FMaterialInstance::setParameterImpl(std::string_view const name,
|
||||
auto const& descriptorSetLayout = mMaterial->getDescriptorSetLayout();
|
||||
DescriptorType const descriptorType = descriptorSetLayout.getDescriptorType(binding);
|
||||
TextureType const textureType = texture->getTextureType();
|
||||
SamplerType const samplerType = texture->getTarget();
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
DescriptorSet::isTextureCompatibleWithDescriptor(textureType, descriptorType))
|
||||
DescriptorSet::isTextureCompatibleWithDescriptor(textureType, samplerType, descriptorType))
|
||||
<< "Texture format " << int(texture->getFormat())
|
||||
<< " of type " << to_string(textureType)
|
||||
<< " with sampler type " << to_string(samplerType)
|
||||
<< " is not compatible with material \"" << getMaterial()->getName().c_str() << "\""
|
||||
<< " parameter \"" << name << "\""
|
||||
<< " of type " << to_string(descriptorType);
|
||||
|
||||
@@ -28,9 +28,19 @@
|
||||
#include "details/Fence.h"
|
||||
#include "details/Scene.h"
|
||||
#include "details/SwapChain.h"
|
||||
#include "details/Texture.h"
|
||||
#include "details/View.h"
|
||||
|
||||
#include "ds/StructureDescriptorSet.h"
|
||||
|
||||
#include "fg/FrameGraph.h"
|
||||
#include "fg/FrameGraphId.h"
|
||||
#include "fg/FrameGraphResources.h"
|
||||
#include "fg/FrameGraphTexture.h"
|
||||
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/Fence.h>
|
||||
#include <filament/Options.h>
|
||||
@@ -41,23 +51,17 @@
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include "fg/FrameGraph.h"
|
||||
#include "fg/FrameGraphId.h"
|
||||
#include "fg/FrameGraphResources.h"
|
||||
#include "fg/FrameGraphTexture.h"
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/architecture.h>
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
@@ -198,6 +202,7 @@ TextureFormat FRenderer::getHdrFormat(const FView& view, bool const translucent)
|
||||
return mHdrQualityHigh;
|
||||
}
|
||||
}
|
||||
return mHdrQualityMedium;
|
||||
}
|
||||
|
||||
TextureFormat FRenderer::getLdrFormat(bool const translucent) const noexcept {
|
||||
@@ -230,7 +235,7 @@ void FRenderer::initializeClearFlags() noexcept {
|
||||
mClearFlags = getClearFlags();
|
||||
}
|
||||
|
||||
void FRenderer::setPresentationTime(int64_t const monotonic_clock_ns) {
|
||||
void FRenderer::setPresentationTime(int64_t const monotonic_clock_ns) const {
|
||||
FEngine::DriverApi& driver = mEngine.getDriverApi();
|
||||
driver.setPresentationTime(monotonic_clock_ns);
|
||||
}
|
||||
@@ -239,6 +244,23 @@ void FRenderer::setVsyncTime(uint64_t const steadyClockTimeNano) noexcept {
|
||||
mVsyncSteadyClockTimeNano = steadyClockTimeNano;
|
||||
}
|
||||
|
||||
std::pair<float, float2> FRenderer::prepareUpscaler(float2 const scale,
|
||||
TemporalAntiAliasingOptions const& taaOptions,
|
||||
DynamicResolutionOptions const& dsrOptions) {
|
||||
float bias = 0.0f;
|
||||
float2 derivativesScale{ 1.0f };
|
||||
if (dsrOptions.enabled && dsrOptions.quality >= QualityLevel::HIGH) {
|
||||
bias = std::log2(std::min(scale.x, scale.y));
|
||||
}
|
||||
if (taaOptions.enabled) {
|
||||
bias += taaOptions.lodBias;
|
||||
if (taaOptions.upscaling) {
|
||||
derivativesScale = 0.5f;
|
||||
}
|
||||
}
|
||||
return { bias, derivativesScale };
|
||||
}
|
||||
|
||||
void FRenderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
|
||||
@@ -301,7 +323,7 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
|
||||
using namespace std::chrono;
|
||||
const steady_clock::time_point now{ steady_clock::now() };
|
||||
const steady_clock::time_point userVsync{ steady_clock::duration(vsyncSteadyClockTimeNano) };
|
||||
const time_point<steady_clock> appVsync(vsyncSteadyClockTimeNano ? userVsync : now);
|
||||
const time_point appVsync(vsyncSteadyClockTimeNano ? userVsync : now);
|
||||
|
||||
mFrameId++;
|
||||
mViewRenderedCount = 0;
|
||||
@@ -341,7 +363,7 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
|
||||
* to ignore the return value and render the frame anyway -- which is perfectly fine.
|
||||
* The remaining work will be done when the first render() call is made.
|
||||
*/
|
||||
auto beginFrameInternal = [this, appVsync, swapChain]() {
|
||||
auto beginFrameInternal = [this, appVsync, swapChain] {
|
||||
FEngine& engine = mEngine;
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
@@ -398,7 +420,7 @@ void FRenderer::endFrame() {
|
||||
FEngine& engine = mEngine;
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
if (UTILS_HAS_THREADING) {
|
||||
if constexpr (UTILS_HAS_THREADING) {
|
||||
// on debug builds this helps to catch cases where we're writing to
|
||||
// the buffer form another thread, which is currently not allowed.
|
||||
driver.debugThreading();
|
||||
@@ -441,7 +463,9 @@ void FRenderer::endFrame() {
|
||||
js.waitAndRelease(job);
|
||||
}
|
||||
|
||||
void FRenderer::readPixels(uint32_t const xoffset, uint32_t const yoffset, uint32_t const width, uint32_t const height,
|
||||
void FRenderer::readPixels(
|
||||
uint32_t const xoffset, uint32_t const yoffset,
|
||||
uint32_t const width, uint32_t const height,
|
||||
PixelBufferDescriptor&& buffer) {
|
||||
|
||||
const bool withinFrame = mSwapChain != nullptr;
|
||||
@@ -453,8 +477,9 @@ void FRenderer::readPixels(uint32_t const xoffset, uint32_t const yoffset, uint3
|
||||
xoffset, yoffset, width, height, std::move(buffer));
|
||||
}
|
||||
|
||||
void FRenderer::readPixels(FRenderTarget* renderTarget,
|
||||
uint32_t const xoffset, uint32_t const yoffset, uint32_t const width, uint32_t const height,
|
||||
void FRenderer::readPixels(FRenderTarget const* renderTarget,
|
||||
uint32_t const xoffset, uint32_t const yoffset,
|
||||
uint32_t const width, uint32_t const height,
|
||||
PixelBufferDescriptor&& buffer) {
|
||||
|
||||
// TODO: change the following to an assert when client call sites have addressed the issue.
|
||||
@@ -791,9 +816,29 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
xvp.bottom = int32_t(guardBand);
|
||||
}
|
||||
|
||||
auto [bias, derivativeScale] = prepareUpscaler(scale, taaOptions, dsrOptions);
|
||||
|
||||
view.prepare(engine, driver, rootArenaScope, svp, cameraInfo, getShaderUserTime(), needsAlphaChannel);
|
||||
|
||||
view.prepareUpscaler(scale, taaOptions, dsrOptions);
|
||||
view.prepareLodBias(bias, derivativeScale);
|
||||
|
||||
// Set the PER_VIEW UBO for the passes that use the user materials, but are not the color pass
|
||||
// (e.g. structure, ssao). The PER_VIEW UBO may be different because these passes don't run
|
||||
// at the same resolution. We set only the values that are relevant to both user-materials
|
||||
// and the concerned passes. The PER_VIEW UBO contains data that is needed by the user
|
||||
// materials public APIs.
|
||||
StructureDescriptorSet& descriptorSet = ppm.getStructureDescriptorSet();
|
||||
descriptorSet.prepareCamera(engine, cameraInfo);
|
||||
descriptorSet.prepareTime(engine, getShaderUserTime());
|
||||
descriptorSet.prepareViewport(svp, {
|
||||
int32_t(float(xvp.left) * aoOptions.resolution),
|
||||
int32_t(float(xvp.bottom) * aoOptions.resolution),
|
||||
uint32_t(float(xvp.width) * aoOptions.resolution),
|
||||
uint32_t(float(xvp.height) * aoOptions.resolution) });
|
||||
descriptorSet.prepareLodBias(bias, derivativeScale);
|
||||
descriptorSet.prepareMaterialGlobals(view.getMaterialGlobals());
|
||||
descriptorSet.commit(driver);
|
||||
|
||||
|
||||
/*
|
||||
* Allocate command buffer
|
||||
@@ -935,49 +980,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
FView::updatePrimitivesLod(scene.getRenderableData(),
|
||||
engine, cameraInfo, view.getVisibleRenderables());
|
||||
|
||||
passBuilder.camera(cameraInfo);
|
||||
passBuilder.camera(cameraInfo.getPosition(), cameraInfo.getForwardVector());
|
||||
passBuilder.geometry(scene.getRenderableData(), view.getVisibleRenderables());
|
||||
|
||||
// view set-ups that need to happen before rendering
|
||||
fg.addTrivialSideEffectPass("Prepare View Uniforms",
|
||||
[=, &view, &engine](DriverApi& driver) {
|
||||
view.prepareCamera(engine, cameraInfo);
|
||||
|
||||
// The code here is a little fragile. In theory, we need to call prepareViewport()
|
||||
// for each render pass, because the viewport parameters depend on the resolution.
|
||||
// However, in practice, we only have two resolutions: the color pass resolution,
|
||||
// and the structure pass which is governed by aoOptions.resolution (this could
|
||||
// change in the future).
|
||||
// So here we set the parameters for the structure pass and SSAO passes which
|
||||
// are always done first. The SSR pass will also use these parameters which
|
||||
// is wrong if it doesn't run at the same resolution as SSAO.
|
||||
// prepareViewport() is called again during the color pass, which resets the
|
||||
// values correctly for the Color pass, however, this will be again wrong
|
||||
// for passes that come after the Color pass, such as DoF.
|
||||
//
|
||||
// The solution is to call prepareViewport() for each pass, really (so we should
|
||||
// move it to its own UBO).
|
||||
//
|
||||
// The reason why this bug is acceptable is that the viewport parameters are
|
||||
// currently only used for generating noise, so it's not too bad.
|
||||
|
||||
// note: aoOptions.resolution is either 1.0 or 0.5, and the result is then
|
||||
// guaranteed to be an integer (because xvp is a multiple of 16).
|
||||
view.prepareViewport(svp,
|
||||
filament::Viewport{
|
||||
int32_t(float(xvp.left ) * aoOptions.resolution),
|
||||
int32_t(float(xvp.bottom) * aoOptions.resolution),
|
||||
uint32_t(float(xvp.width ) * aoOptions.resolution),
|
||||
uint32_t(float(xvp.height) * aoOptions.resolution)});
|
||||
|
||||
// this needs to reset the sampler that are only set in RendererUtils::colorPass(), because
|
||||
// this descriptor-set is also used for ssr/picking/structure and these could be stale
|
||||
// it would be better to use a separate desriptor-set for those two cases so that we don't
|
||||
// have to do this
|
||||
view.unbindSamplers(engine);
|
||||
view.commitUniformsAndSamplers(driver);
|
||||
});
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// structure pass -- automatically culled if not used
|
||||
// Currently it consists of a simple depth pass.
|
||||
@@ -998,68 +1003,20 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
|
||||
if (view.hasPicking()) {
|
||||
if (view.isTransparentPickingEnabled()) {
|
||||
struct PickingRenderPassData {
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
FrameGraphId<FrameGraphTexture> picking;
|
||||
};
|
||||
auto& pickingRenderPass = fg.addPass<PickingRenderPassData>("Picking Render Pass",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
bool const isFL0 = mEngine.getDriverApi().getFeatureLevel() ==
|
||||
FeatureLevel::FEATURE_LEVEL_0;
|
||||
|
||||
// TODO: Specify the precision for picking pass
|
||||
uint32_t const width = std::max(32u,
|
||||
(uint32_t)std::ceil(float(svp.width) * aoOptions.resolution));
|
||||
uint32_t const height = std::max(32u,
|
||||
(uint32_t)std::ceil(float(svp.height) * aoOptions.resolution));
|
||||
data.depth = builder.createTexture("Depth Buffer", {
|
||||
.width = width, .height = height,
|
||||
.format = isFL0 ? TextureFormat::DEPTH24 : TextureFormat::DEPTH32F });
|
||||
|
||||
data.depth = builder.write(data.depth,
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
data.picking = builder.createTexture("Picking Buffer", {
|
||||
.width = width, .height = height,
|
||||
.format = isFL0 ? TextureFormat::RGBA8 : TextureFormat::RG32F });
|
||||
|
||||
data.picking = builder.write(data.picking,
|
||||
FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
|
||||
builder.declareRenderPass("Picking Render Target", {
|
||||
.attachments = {.color = { data.picking }, .depth = data.depth },
|
||||
.clearFlags = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH
|
||||
});
|
||||
},
|
||||
[=, passBuilder = passBuilder](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) mutable {
|
||||
Variant pickingVariant(Variant::DEPTH_VARIANT);
|
||||
pickingVariant.setPicking(true);
|
||||
|
||||
auto out = resources.getRenderPassInfo();
|
||||
passBuilder.renderFlags(renderFlags);
|
||||
passBuilder.variant(pickingVariant);
|
||||
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::DEPTH);
|
||||
|
||||
RenderPass const pass{ passBuilder.build(mEngine, driver) };
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
pass.getExecutor().execute(mEngine, driver);
|
||||
driver.endRenderPass();
|
||||
});
|
||||
picking = pickingRenderPass->picking;
|
||||
picking = ppm.transparentPicking(fg,
|
||||
passBuilder, renderFlags, svp.width, svp.height, aoOptions.resolution);
|
||||
}
|
||||
|
||||
struct PickingResolvePassData {
|
||||
FrameGraphId<FrameGraphTexture> picking;
|
||||
};
|
||||
fg.addPass<PickingResolvePassData>(
|
||||
"Picking Resolve Pass",
|
||||
fg.addPass<PickingResolvePassData>("Picking Resolve Pass",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
// Note that BLIT_SRC is needed because this texture will be read later (via
|
||||
// readPixels()).
|
||||
data.picking =
|
||||
builder.read(picking, FrameGraphTexture::Usage::COLOR_ATTACHMENT |
|
||||
FrameGraphTexture::Usage::BLIT_SRC);
|
||||
data.picking = builder.read(picking,
|
||||
FrameGraphTexture::Usage::COLOR_ATTACHMENT |
|
||||
FrameGraphTexture::Usage::BLIT_SRC);
|
||||
builder.declareRenderPass("Picking Resolve Target", {
|
||||
.attachments = { .color = { data.picking }}
|
||||
});
|
||||
@@ -1067,23 +1024,21 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
},
|
||||
[=, &view](FrameGraphResources const& resources,
|
||||
auto const&, DriverApi& driver) mutable {
|
||||
auto out = resources.getRenderPassInfo();
|
||||
view.executePickingQueries(driver, out.target, scale * aoOptions.resolution);
|
||||
auto [target, params] = resources.getRenderPassInfo();
|
||||
view.executePickingQueries(driver, target, scale * aoOptions.resolution);
|
||||
});
|
||||
}
|
||||
|
||||
// Store this frame's camera projection in the frame history.
|
||||
// TODO: We do this after we're configured the structure and ssao passes;
|
||||
// but I'm not 100% sure this should be done before or after.
|
||||
if (UTILS_UNLIKELY(taaOptions.enabled)) {
|
||||
// Apply the TAA jitter to everything after the structure pass, starting with the color pass.
|
||||
ppm.TaaJitterCamera(svp, taaOptions, view.getFrameHistory(),
|
||||
&FrameHistoryEntry::taa, &cameraInfo);
|
||||
|
||||
fg.addTrivialSideEffectPass("Jitter Camera",
|
||||
[&engine, &cameraInfo, &descriptorSet = view.getColorPassDescriptorSet()]
|
||||
(DriverApi& driver) {
|
||||
descriptorSet.prepareCamera(engine, cameraInfo);
|
||||
descriptorSet.commit(driver);
|
||||
});
|
||||
// this just re-set the color pass UBO content
|
||||
view.prepareCamera(engine, cameraInfo);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
@@ -1129,7 +1084,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
// (i.e. it won't be culled, unless everything is culled), so no need to complexify things.
|
||||
passBuilder.variant(variant);
|
||||
|
||||
// This is optional, if not set, the per-view descriptor-set must be set before calling execute()
|
||||
// We need to specify the ColorPassDescriptorSet (which is in fact a collection of descriptor
|
||||
// sets) because the layout can change based on the material used, and that's only known
|
||||
// at execution time.
|
||||
passBuilder.colorPassDescriptorSet(&view.getColorPassDescriptorSet());
|
||||
|
||||
// color-grading as subpass is done either by the color pass or the TAA pass if any
|
||||
@@ -1141,7 +1098,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
passBuilder.customCommand(3,
|
||||
RenderPass::Pass::BLENDED,
|
||||
RenderPass::CustomCommand::EPILOG,
|
||||
0, [&ppm, &driver, colorGradingConfigForColor]() {
|
||||
0, [&ppm, &driver, colorGradingConfigForColor] {
|
||||
ppm.colorGradingSubpass(driver, colorGradingConfigForColor);
|
||||
});
|
||||
} else if (colorGradingConfig.customResolve) {
|
||||
@@ -1149,7 +1106,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
passBuilder.customCommand(3,
|
||||
RenderPass::Pass::BLENDED,
|
||||
RenderPass::CustomCommand::EPILOG,
|
||||
0, [&ppm, &driver]() {
|
||||
0, [&ppm, &driver] {
|
||||
ppm.customResolveSubpass(driver);
|
||||
});
|
||||
}
|
||||
@@ -1220,8 +1177,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
|
||||
// We use a framegraph pass to wait for froxelization to finish (so it can be done
|
||||
// in parallel with .compile()
|
||||
auto sync = view.getFroxelizerSync();
|
||||
if (sync) {
|
||||
if (auto sync = view.getFroxelizerSync()) {
|
||||
js.waitAndRelease(sync);
|
||||
view.commitFroxels(driver);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
void renderStandaloneView(FView const* view);
|
||||
|
||||
|
||||
void setPresentationTime(int64_t monotonic_clock_ns);
|
||||
void setPresentationTime(int64_t monotonic_clock_ns) const;
|
||||
|
||||
void setVsyncTime(uint64_t steadyClockTimeNano) noexcept;
|
||||
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
backend::PixelBufferDescriptor&& buffer);
|
||||
|
||||
// read pixel from a rendertarget. must be called between beginFrame/enfFrame.
|
||||
void readPixels(FRenderTarget* renderTarget,
|
||||
void readPixels(FRenderTarget const* renderTarget,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer);
|
||||
|
||||
@@ -189,6 +189,10 @@ private:
|
||||
void renderInternal(FView const* view);
|
||||
void renderJob(RootArenaScope& rootArenaScope, FView& view);
|
||||
|
||||
std::pair<float, math::float2> prepareUpscaler(math::float2 scale,
|
||||
TemporalAntiAliasingOptions const& taaOptions,
|
||||
DynamicResolutionOptions const& dsrOptions);
|
||||
|
||||
// keep a reference to our engine
|
||||
FEngine& mEngine;
|
||||
FrameSkipper mFrameSkipper;
|
||||
|
||||
@@ -16,11 +16,15 @@
|
||||
|
||||
#include "details/View.h"
|
||||
|
||||
#include "Allocators.h"
|
||||
#include "Culler.h"
|
||||
#include "DebugRegistry.h"
|
||||
#include "FrameHistory.h"
|
||||
#include "FrameInfo.h"
|
||||
#include "Froxelizer.h"
|
||||
#include "RenderPrimitive.h"
|
||||
#include "ResourceAllocator.h"
|
||||
#include "ShadowMap.h"
|
||||
#include "ShadowMapManager.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
@@ -30,9 +34,15 @@
|
||||
#include "details/Scene.h"
|
||||
#include "details/Skybox.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <fg/FrameGraphTexture.h>
|
||||
#include <fg/FrameGraphId.h>
|
||||
|
||||
#include <filament/Exposure.h>
|
||||
#include <filament/Frustum.h>
|
||||
#include <filament/DebugRegistry.h>
|
||||
#include <filament/TextureSampler.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <private/filament/UibStructs.h>
|
||||
@@ -40,18 +50,29 @@
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/architecture.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Profiler.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/Zip2Iterator.h>
|
||||
|
||||
#include <math/mat3.h>
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
#include <math/scalar.h>
|
||||
#include <math/fast.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <ratio>
|
||||
#include <utility>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -114,7 +135,7 @@ FView::FView(FEngine& engine)
|
||||
#ifndef NDEBUG
|
||||
// This can fail if another view has already registered this data source
|
||||
mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info",
|
||||
[weak = std::weak_ptr<DebugState>(mDebugState)]() -> DebugRegistry::DataSource {
|
||||
[weak = std::weak_ptr(mDebugState)]() -> DebugRegistry::DataSource {
|
||||
// the View could have been destroyed by the time we do this
|
||||
auto const state = weak.lock();
|
||||
if (!state) {
|
||||
@@ -200,8 +221,8 @@ void FView::terminate(FEngine& engine) {
|
||||
|
||||
void FView::setViewport(filament::Viewport const& viewport) noexcept {
|
||||
// catch the cases were user had an underflow and didn't catch it.
|
||||
assert((int32_t)viewport.width > 0);
|
||||
assert((int32_t)viewport.height > 0);
|
||||
assert(int32_t(viewport.width) > 0);
|
||||
assert(int32_t(viewport.height) > 0);
|
||||
mViewport = viewport;
|
||||
}
|
||||
|
||||
@@ -274,7 +295,7 @@ float2 FView::updateScale(FEngine& engine,
|
||||
const float dt = 1.0f; // we don't really need dt here, setting it to 1, means our parameters are in "frames"
|
||||
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
|
||||
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
|
||||
float const measured = duration<float, std::milli>{ info.denoisedFrameTime }.count();
|
||||
float const measured = duration{ info.denoisedFrameTime }.count();
|
||||
float const out = mPidController.update(measured / targetWithHeadroom, 1.0f, dt);
|
||||
|
||||
// maps pid command to a scale (absolute or relative, see below)
|
||||
@@ -433,7 +454,7 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
|
||||
|
||||
if (builder.hasShadowMaps()) {
|
||||
ShadowMapManager::createIfNeeded(engine, mShadowMapManager);
|
||||
auto shadowTechnique = mShadowMapManager->update(builder, engine, *this,
|
||||
auto const shadowTechnique = mShadowMapManager->update(builder, engine, *this,
|
||||
cameraInfo, renderableData, lightData);
|
||||
|
||||
mHasShadowing = any(shadowTechnique);
|
||||
@@ -493,7 +514,7 @@ void FView::prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexc
|
||||
getColorPassDescriptorSet().prepareDirectionalLight(engine, exposure, sceneSpaceDirection, directionalLight);
|
||||
}
|
||||
|
||||
CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
|
||||
CameraInfo FView::computeCameraInfo(FEngine const& engine) const noexcept {
|
||||
FScene const* const scene = getScene();
|
||||
|
||||
/*
|
||||
@@ -546,15 +567,14 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
|
||||
// In the common case when we don't have a viewing camera, cameraInfo.view is
|
||||
// already the culling view matrix
|
||||
return Frustum{ mat4f{ highPrecisionMultiply(cameraInfo.cullingProjection, cameraInfo.view) }};
|
||||
} else {
|
||||
// Otherwise, we need to recalculate it from the culling camera.
|
||||
// Note: it is correct to always do the math from mCullingCamera, but it hides the
|
||||
// intent of the code, which is that we should only depend on CameraInfo here.
|
||||
// This is an extremely uncommon case.
|
||||
const mat4 projection = mCullingCamera->getCullingProjectionMatrix();
|
||||
const mat4 view = inverse(cameraInfo.worldTransform * mCullingCamera->getModelMatrix());
|
||||
return Frustum{ mat4f{ projection * view }};
|
||||
}
|
||||
// Otherwise, we need to recalculate it from the culling camera.
|
||||
// Note: it is correct to always do the math from mCullingCamera, but it hides the
|
||||
// intent of the code, which is that we should only depend on CameraInfo here.
|
||||
// This is an extremely uncommon case.
|
||||
const mat4 projection = mCullingCamera->getCullingProjectionMatrix();
|
||||
const mat4 view = inverse(cameraInfo.worldTransform * mCullingCamera->getModelMatrix());
|
||||
return Frustum{ mat4f{ projection * view }};
|
||||
};
|
||||
|
||||
const Frustum cullingFrustum = getFrustum();
|
||||
@@ -599,9 +619,9 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
|
||||
setFroxelizerSync(froxelizeLightsJob);
|
||||
|
||||
Range merged;
|
||||
FScene::RenderableSoa& renderableData = scene->getRenderableData();
|
||||
|
||||
{ // all the operations in this scope must happen sequentially
|
||||
FScene::RenderableSoa& renderableData = scene->getRenderableData();
|
||||
|
||||
Slice<Culler::result_type> cullingMask = renderableData.slice<FScene::VISIBLE_MASK>();
|
||||
std::uninitialized_fill(cullingMask.begin(), cullingMask.end(), 0);
|
||||
@@ -646,7 +666,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
|
||||
}
|
||||
// We need to pass viewMatrix by value here because it extends the scope of this
|
||||
// function.
|
||||
std::function<void(JobSystem&, JobSystem::Job*)> froxelizerWork =
|
||||
std::function froxelizerWork =
|
||||
[&froxelizer = mFroxelizer, &engine, viewMatrix = cameraInfo.view, &lightData]
|
||||
(JobSystem&, JobSystem::Job*) {
|
||||
froxelizer.froxelizeLights(engine, viewMatrix, lightData);
|
||||
@@ -854,6 +874,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
|
||||
auto const fogTransform = tcm.getWorldTransformAccurate(tcm.getInstance(mFogEntity));
|
||||
|
||||
auto& colorPassDescriptorSet = getColorPassDescriptorSet();
|
||||
colorPassDescriptorSet.prepareCamera(engine, cameraInfo);
|
||||
colorPassDescriptorSet.prepareTime(engine, userTime);
|
||||
colorPassDescriptorSet.prepareFog(engine, cameraInfo, fogTransform, mFogOptions,
|
||||
scene->getIndirectLight());
|
||||
@@ -905,29 +926,15 @@ UTILS_NOINLINE
|
||||
});
|
||||
}
|
||||
|
||||
void FView::prepareUpscaler(float2 const scale,
|
||||
TemporalAntiAliasingOptions const& taaOptions,
|
||||
DynamicResolutionOptions const& dsrOptions) const noexcept {
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
float bias = 0.0f;
|
||||
float2 derivativesScale{ 1.0f };
|
||||
if (dsrOptions.enabled && dsrOptions.quality >= QualityLevel::HIGH) {
|
||||
bias = std::log2(std::min(scale.x, scale.y));
|
||||
}
|
||||
if (taaOptions.enabled) {
|
||||
bias += taaOptions.lodBias;
|
||||
if (taaOptions.upscaling) {
|
||||
derivativesScale = 0.5f;
|
||||
}
|
||||
}
|
||||
getColorPassDescriptorSet().prepareLodBias(bias, derivativesScale);
|
||||
}
|
||||
|
||||
void FView::prepareCamera(FEngine& engine, const CameraInfo& cameraInfo) const noexcept {
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
getColorPassDescriptorSet().prepareCamera(engine, cameraInfo);
|
||||
}
|
||||
|
||||
void FView::prepareLodBias(float const bias, float2 const derivativesScale) const noexcept {
|
||||
getColorPassDescriptorSet().prepareLodBias(bias, derivativesScale);
|
||||
}
|
||||
|
||||
void FView::prepareViewport(
|
||||
const filament::Viewport& physicalViewport,
|
||||
const filament::Viewport& logicalViewport) const noexcept {
|
||||
@@ -1120,7 +1127,7 @@ void FView::prepareVisibleLights(FLightManager const& lcm,
|
||||
computeLightCameraDistances(distances, viewMatrix, spheres, visibleLightCount);
|
||||
|
||||
// skip directional light
|
||||
Zip2Iterator<FScene::LightSoa::iterator, float*> b = { lightData.begin(), distances };
|
||||
Zip2Iterator b = { lightData.begin(), distances };
|
||||
std::sort(b + FScene::DIRECTIONAL_LIGHTS_COUNT, b + visibleLightCount,
|
||||
[](auto const& lhs, auto const& rhs) { return lhs.second < rhs.second; });
|
||||
}
|
||||
@@ -1290,8 +1297,8 @@ void FView::setAmbientOcclusionOptions(AmbientOcclusionOptions options) noexcept
|
||||
options.ssct.lightDirection = normalize(options.ssct.lightDirection);
|
||||
options.ssct.depthBias = std::max(0.0f, options.ssct.depthBias);
|
||||
options.ssct.depthSlopeBias = std::max(0.0f, options.ssct.depthSlopeBias);
|
||||
options.ssct.sampleCount = clamp((unsigned)options.ssct.sampleCount, 1u, 255u);
|
||||
options.ssct.rayCount = clamp((unsigned)options.ssct.rayCount, 1u, 255u);
|
||||
options.ssct.sampleCount = clamp(unsigned(options.ssct.sampleCount), 1u, 255u);
|
||||
options.ssct.rayCount = clamp(unsigned(options.ssct.rayCount), 1u, 255u);
|
||||
mAmbientOcclusionOptions = options;
|
||||
}
|
||||
void FView::setVsmShadowOptions(VsmShadowOptions options) noexcept {
|
||||
@@ -1319,7 +1326,6 @@ void FView::setFogOptions(FogOptions options) noexcept {
|
||||
options.maximumOpacity = clamp(options.maximumOpacity, 0.0f, 1.0f);
|
||||
options.density = std::max(0.0f, options.density);
|
||||
options.heightFalloff = std::max(0.0f, options.heightFalloff);
|
||||
options.inScatteringSize = options.inScatteringSize;
|
||||
options.inScatteringStart = std::max(0.0f, options.inScatteringStart);
|
||||
mFogOptions = options;
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ class FScene;
|
||||
|
||||
class FView : public View {
|
||||
public:
|
||||
using MaterialGlobals = std::array<math::float4, 4>;
|
||||
using Range = utils::Range<uint32_t>;
|
||||
|
||||
explicit FView(FEngine& engine);
|
||||
@@ -109,7 +110,7 @@ public:
|
||||
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
CameraInfo computeCameraInfo(FEngine& engine) const noexcept;
|
||||
CameraInfo computeCameraInfo(FEngine const& engine) const noexcept;
|
||||
|
||||
// note: viewport/cameraInfo are passed by value to make it clear that prepare cannot
|
||||
// keep references on them that would outlive the scope of prepare() (e.g. with JobSystem).
|
||||
@@ -159,11 +160,10 @@ public:
|
||||
return mName.c_str_safe();
|
||||
}
|
||||
|
||||
void prepareUpscaler(math::float2 scale,
|
||||
TemporalAntiAliasingOptions const& taaOptions,
|
||||
DynamicResolutionOptions const& dsrOptions) const noexcept;
|
||||
void prepareCamera(FEngine& engine, const CameraInfo& cameraInfo) const noexcept;
|
||||
|
||||
void prepareLodBias(float bias, math::float2 derivativesScale) const noexcept;
|
||||
|
||||
void prepareViewport(
|
||||
const Viewport& physicalViewport,
|
||||
const Viewport& logicalViewport) const noexcept;
|
||||
@@ -483,6 +483,8 @@ public:
|
||||
return mFrameGraphViewerViewHandle;
|
||||
}
|
||||
|
||||
MaterialGlobals getMaterialGlobals() const { return mMaterialGlobals; }
|
||||
|
||||
private:
|
||||
struct FPickingQuery : public PickingQuery {
|
||||
private:
|
||||
@@ -609,14 +611,14 @@ private:
|
||||
|
||||
std::unique_ptr<ShadowMapManager> mShadowMapManager;
|
||||
|
||||
std::array<math::float4, 4> mMaterialGlobals = {{
|
||||
{ 0, 0, 0, 1 },
|
||||
{ 0, 0, 0, 1 },
|
||||
{ 0, 0, 0, 1 },
|
||||
{ 0, 0, 0, 1 },
|
||||
}};
|
||||
MaterialGlobals mMaterialGlobals = {{
|
||||
{ 0, 0, 0, 1 },
|
||||
{ 0, 0, 0, 1 },
|
||||
{ 0, 0, 0, 1 },
|
||||
{ 0, 0, 0, 1 },
|
||||
}};
|
||||
|
||||
fgviewer::ViewHandle mFrameGraphViewerViewHandle;
|
||||
fgviewer::ViewHandle mFrameGraphViewerViewHandle{};
|
||||
|
||||
#ifndef NDEBUG
|
||||
struct DebugState {
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
#include "ColorPassDescriptorSet.h"
|
||||
|
||||
|
||||
#include "Froxelizer.h"
|
||||
#include "PerViewDescriptorSetUtils.h"
|
||||
#include "HwDescriptorSetLayoutFactory.h"
|
||||
#include "ShadowMapManager.h"
|
||||
#include "TypedUniformBuffer.h"
|
||||
@@ -144,44 +146,11 @@ void ColorPassDescriptorSet::terminate(HwDescriptorSetLayoutFactory& factory, Dr
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareCamera(FEngine& engine, const CameraInfo& camera) noexcept {
|
||||
mat4f const& viewFromWorld = camera.view;
|
||||
mat4f const& worldFromView = camera.model;
|
||||
mat4f const& clipFromView = camera.projection;
|
||||
|
||||
const mat4f viewFromClip{ inverse((mat4)camera.projection) };
|
||||
const mat4f worldFromClip{ highPrecisionMultiply(worldFromView, viewFromClip) };
|
||||
|
||||
auto& s = mUniforms.edit();
|
||||
s.viewFromWorldMatrix = viewFromWorld; // view
|
||||
s.worldFromViewMatrix = worldFromView; // model
|
||||
s.clipFromViewMatrix = clipFromView; // projection
|
||||
s.viewFromClipMatrix = viewFromClip; // 1/projection
|
||||
s.worldFromClipMatrix = worldFromClip; // 1/(projection * view)
|
||||
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldTransform));
|
||||
s.clipTransform = camera.clipTransform;
|
||||
s.cameraFar = camera.zf;
|
||||
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
|
||||
s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn);
|
||||
|
||||
mat4f const& headFromWorld = camera.view;
|
||||
Engine::Config const& config = engine.getConfig();
|
||||
for (int i = 0; i < config.stereoscopicEyeCount; i++) {
|
||||
mat4f const& eyeFromHead = camera.eyeFromView[i]; // identity for monoscopic rendering
|
||||
mat4f const& clipFromEye = camera.eyeProjection[i];
|
||||
// clipFromEye * eyeFromHead * headFromWorld
|
||||
s.clipFromWorldMatrix[i] = highPrecisionMultiply(
|
||||
clipFromEye, highPrecisionMultiply(eyeFromHead, headFromWorld));
|
||||
}
|
||||
|
||||
// with a clip-space of [-w, w] ==> z' = -z
|
||||
// with a clip-space of [0, w] ==> z' = (w - z)/2
|
||||
s.clipControl = engine.getDriverApi().getClipSpaceParams();
|
||||
PerViewDescriptorSetUtils::prepareCamera(mUniforms.edit(), engine, camera);
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareLodBias(float const bias, float2 const derivativesScale) noexcept {
|
||||
auto& s = mUniforms.edit();
|
||||
s.lodBias = bias;
|
||||
s.derivativesScale = derivativesScale;
|
||||
PerViewDescriptorSetUtils::prepareLodBias(mUniforms.edit(), bias, derivativesScale);
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareExposure(float const ev100) noexcept {
|
||||
@@ -194,22 +163,11 @@ void ColorPassDescriptorSet::prepareExposure(float const ev100) noexcept {
|
||||
void ColorPassDescriptorSet::prepareViewport(
|
||||
const filament::Viewport& physicalViewport,
|
||||
const filament::Viewport& logicalViewport) noexcept {
|
||||
float4 const physical{ physicalViewport.left, physicalViewport.bottom,
|
||||
physicalViewport.width, physicalViewport.height };
|
||||
float4 const logical{ logicalViewport.left, logicalViewport.bottom,
|
||||
logicalViewport.width, logicalViewport.height };
|
||||
auto& s = mUniforms.edit();
|
||||
s.resolution = { physical.zw, 1.0f / physical.zw };
|
||||
s.logicalViewportScale = physical.zw / logical.zw;
|
||||
s.logicalViewportOffset = -logical.xy / logical.zw;
|
||||
PerViewDescriptorSetUtils::prepareViewport(mUniforms.edit(), physicalViewport, logicalViewport);
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareTime(FEngine& engine, float4 const& userTime) noexcept {
|
||||
auto& s = mUniforms.edit();
|
||||
const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1000000000;
|
||||
const float fraction = float(double(oneSecondRemainder) / 1000000000.0);
|
||||
s.time = fraction;
|
||||
s.userTime = userTime;
|
||||
PerViewDescriptorSetUtils::prepareTime(mUniforms.edit(), engine, userTime);
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareTemporalNoise(FEngine& engine,
|
||||
@@ -324,10 +282,7 @@ void ColorPassDescriptorSet::prepareBlending(bool const needsAlphaChannel) noexc
|
||||
|
||||
void ColorPassDescriptorSet::prepareMaterialGlobals(
|
||||
std::array<float4, 4> const& materialGlobals) noexcept {
|
||||
mUniforms.edit().custom[0] = materialGlobals[0];
|
||||
mUniforms.edit().custom[1] = materialGlobals[1];
|
||||
mUniforms.edit().custom[2] = materialGlobals[2];
|
||||
mUniforms.edit().custom[3] = materialGlobals[3];
|
||||
PerViewDescriptorSetUtils::prepareMaterialGlobals(mUniforms.edit(), materialGlobals);
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareSSR(Handle<HwTexture> ssr,
|
||||
@@ -345,25 +300,6 @@ void ColorPassDescriptorSet::prepareSSR(Handle<HwTexture> ssr,
|
||||
s.ssrDistance = (ssrOptions.enabled && !disableSSR) ? ssrOptions.maxDistance : 0.0f;
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareHistorySSR(Handle<HwTexture> ssr,
|
||||
mat4f const& historyProjection,
|
||||
mat4f const& uvFromViewMatrix,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept {
|
||||
|
||||
setSampler(+PerViewBindingPoints::SSR, ssr, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
|
||||
auto& s = mUniforms.edit();
|
||||
s.ssrReprojection = historyProjection;
|
||||
s.ssrUvFromViewMatrix = uvFromViewMatrix;
|
||||
s.ssrThickness = ssrOptions.thickness;
|
||||
s.ssrBias = ssrOptions.bias;
|
||||
s.ssrDistance = ssrOptions.enabled ? ssrOptions.maxDistance : 0.0f;
|
||||
s.ssrStride = ssrOptions.stride;
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareStructure(Handle<HwTexture> structure) noexcept {
|
||||
// sampler must be NEAREST
|
||||
setSampler(+PerViewBindingPoints::STRUCTURE, structure, {});
|
||||
|
||||
@@ -117,11 +117,6 @@ public:
|
||||
float refractionLodOffset,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
|
||||
|
||||
void prepareHistorySSR(TextureHandle ssr,
|
||||
math::mat4f const& historyProjection,
|
||||
math::mat4f const& uvFromViewMatrix,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
|
||||
|
||||
void prepareShadowMapping(backend::BufferObjectHandle shadowUniforms, bool highPrecision) noexcept;
|
||||
|
||||
void prepareDirectionalLight(FEngine& engine, float exposure,
|
||||
|
||||
@@ -73,8 +73,7 @@ DescriptorSet& DescriptorSet::operator=(DescriptorSet&& rhs) noexcept {
|
||||
|
||||
void DescriptorSet::terminate(FEngine::DriverApi& driver) noexcept {
|
||||
if (mDescriptorSetHandle) {
|
||||
driver.destroyDescriptorSet(mDescriptorSetHandle);
|
||||
mDescriptorSetHandle.clear();
|
||||
driver.destroyDescriptorSet(std::move(mDescriptorSetHandle));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,9 +197,40 @@ DescriptorSet DescriptorSet::duplicate(
|
||||
return set;
|
||||
}
|
||||
bool DescriptorSet::isTextureCompatibleWithDescriptor(
|
||||
backend::TextureType t, backend::DescriptorType d) noexcept {
|
||||
backend::TextureType t, backend::SamplerType s, backend::DescriptorType d) noexcept {
|
||||
using namespace backend;
|
||||
|
||||
switch (s) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
if (!is2dTypeDescriptor(d)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
if (!is2dArrayTypeDescriptor(d)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
if (!isCubeTypeDescriptor(d)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
if (!isCubeArrayTypeDescriptor(d)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
if (!is3dTypeDescriptor(d)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
break;
|
||||
}
|
||||
|
||||
// check that the descriptor type is compatible with the texture format type
|
||||
switch (d) {
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
|
||||
@@ -86,7 +86,8 @@ public:
|
||||
}
|
||||
|
||||
static bool isTextureCompatibleWithDescriptor(
|
||||
backend::TextureType t, backend::DescriptorType d) noexcept;
|
||||
backend::TextureType t, backend::SamplerType s,
|
||||
backend::DescriptorType d) noexcept;
|
||||
|
||||
private:
|
||||
struct Desc {
|
||||
|
||||
110
filament/src/ds/PerViewDescriptorSetUtils.cpp
Normal file
110
filament/src/ds/PerViewDescriptorSetUtils.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "PerViewDescriptorSetUtils.h"
|
||||
|
||||
#include "details/Camera.h"
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
void PerViewDescriptorSetUtils::prepareCamera(PerViewUib& s,
|
||||
FEngine const& engine, const CameraInfo& camera) noexcept {
|
||||
mat4f const& viewFromWorld = camera.view;
|
||||
mat4f const& worldFromView = camera.model;
|
||||
mat4f const& clipFromView = camera.projection;
|
||||
|
||||
const mat4f viewFromClip{ inverse((mat4)camera.projection) };
|
||||
const mat4f worldFromClip{ highPrecisionMultiply(worldFromView, viewFromClip) };
|
||||
|
||||
s.viewFromWorldMatrix = viewFromWorld; // view
|
||||
s.worldFromViewMatrix = worldFromView; // model
|
||||
s.clipFromViewMatrix = clipFromView; // projection
|
||||
s.viewFromClipMatrix = viewFromClip; // 1/projection
|
||||
s.worldFromClipMatrix = worldFromClip; // 1/(projection * view)
|
||||
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldTransform));
|
||||
s.clipTransform = camera.clipTransform;
|
||||
s.cameraFar = camera.zf;
|
||||
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
|
||||
s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn);
|
||||
|
||||
mat4f const& headFromWorld = camera.view;
|
||||
Engine::Config const& config = engine.getConfig();
|
||||
for (int i = 0; i < config.stereoscopicEyeCount; i++) {
|
||||
mat4f const& eyeFromHead = camera.eyeFromView[i]; // identity for monoscopic rendering
|
||||
mat4f const& clipFromEye = camera.eyeProjection[i];
|
||||
// clipFromEye * eyeFromHead * headFromWorld
|
||||
s.clipFromWorldMatrix[i] = highPrecisionMultiply(
|
||||
clipFromEye, highPrecisionMultiply(eyeFromHead, headFromWorld));
|
||||
}
|
||||
|
||||
// with a clip-space of [-w, w] ==> z' = -z
|
||||
// with a clip-space of [0, w] ==> z' = (w - z)/2
|
||||
s.clipControl = const_cast<FEngine&>(engine).getDriverApi().getClipSpaceParams();
|
||||
}
|
||||
|
||||
void PerViewDescriptorSetUtils::prepareLodBias(PerViewUib& s, float bias,
|
||||
float2 derivativesScale) noexcept {
|
||||
s.lodBias = bias;
|
||||
s.derivativesScale = derivativesScale;
|
||||
}
|
||||
|
||||
void PerViewDescriptorSetUtils::prepareViewport(PerViewUib& s,
|
||||
backend::Viewport const& physicalViewport,
|
||||
backend::Viewport const& logicalViewport) noexcept {
|
||||
float4 const physical{ physicalViewport.left, physicalViewport.bottom,
|
||||
physicalViewport.width, physicalViewport.height };
|
||||
float4 const logical{ logicalViewport.left, logicalViewport.bottom,
|
||||
logicalViewport.width, logicalViewport.height };
|
||||
s.resolution = { physical.zw, 1.0f / physical.zw };
|
||||
s.logicalViewportScale = physical.zw / logical.zw;
|
||||
s.logicalViewportOffset = -logical.xy / logical.zw;
|
||||
}
|
||||
|
||||
void PerViewDescriptorSetUtils::prepareTime(PerViewUib& s,
|
||||
FEngine const& engine, float4 const& userTime) noexcept {
|
||||
const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1000000000;
|
||||
const float fraction = float(double(oneSecondRemainder) / 1000000000.0);
|
||||
s.time = fraction;
|
||||
s.userTime = userTime;
|
||||
}
|
||||
|
||||
void PerViewDescriptorSetUtils::prepareMaterialGlobals(PerViewUib& s,
|
||||
std::array<float4, 4> const& materialGlobals) noexcept {
|
||||
s.custom[0] = materialGlobals[0];
|
||||
s.custom[1] = materialGlobals[1];
|
||||
s.custom[2] = materialGlobals[2];
|
||||
s.custom[3] = materialGlobals[3];
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
53
filament/src/ds/PerViewDescriptorSetUtils.h
Normal file
53
filament/src/ds/PerViewDescriptorSetUtils.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
struct Viewport;
|
||||
} // namespace backend
|
||||
|
||||
class FEngine;
|
||||
struct CameraInfo;
|
||||
|
||||
class PerViewDescriptorSetUtils {
|
||||
public:
|
||||
static void prepareCamera(PerViewUib& uniforms,
|
||||
FEngine const& engine, const CameraInfo& camera) noexcept;
|
||||
|
||||
static void prepareLodBias(PerViewUib& uniforms,
|
||||
float bias, math::float2 derivativesScale) noexcept;
|
||||
|
||||
static void prepareViewport(PerViewUib& uniforms,
|
||||
backend::Viewport const& physicalViewport,
|
||||
backend::Viewport const& logicalViewport) noexcept;
|
||||
|
||||
static void prepareTime(PerViewUib& uniforms,
|
||||
FEngine const& engine, math::float4 const& userTime) noexcept;
|
||||
|
||||
static void prepareMaterialGlobals(PerViewUib& uniforms,
|
||||
std::array<math::float4, 4> const& materialGlobals) noexcept;
|
||||
};
|
||||
|
||||
} //namespace filament
|
||||
@@ -16,20 +16,21 @@
|
||||
|
||||
#include "ShadowMapDescriptorSet.h"
|
||||
|
||||
#include "PerViewDescriptorSetUtils.h"
|
||||
|
||||
#include "details/Camera.h"
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/DescriptorSets.h>
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <array>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -62,54 +63,30 @@ PerViewUib& ShadowMapDescriptorSet::edit(Transaction const& transaction) noexcep
|
||||
}
|
||||
|
||||
void ShadowMapDescriptorSet::prepareCamera(Transaction const& transaction,
|
||||
DriverApi& driver, const CameraInfo& camera) noexcept {
|
||||
mat4f const& viewFromWorld = camera.view;
|
||||
mat4f const& worldFromView = camera.model;
|
||||
mat4f const& clipFromView = camera.projection;
|
||||
|
||||
const mat4f viewFromClip{ inverse((mat4)camera.projection) };
|
||||
const mat4f clipFromWorld{ highPrecisionMultiply(clipFromView, viewFromWorld) };
|
||||
const mat4f worldFromClip{ highPrecisionMultiply(worldFromView, viewFromClip) };
|
||||
|
||||
auto& s = edit(transaction);
|
||||
s.viewFromWorldMatrix = viewFromWorld; // view
|
||||
s.worldFromViewMatrix = worldFromView; // model
|
||||
s.clipFromViewMatrix = clipFromView; // projection
|
||||
s.viewFromClipMatrix = viewFromClip; // 1/projection
|
||||
s.clipFromWorldMatrix[0] = clipFromWorld; // projection * view
|
||||
s.worldFromClipMatrix = worldFromClip; // 1/(projection * view)
|
||||
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldTransform));
|
||||
s.clipTransform = camera.clipTransform;
|
||||
s.cameraFar = camera.zf;
|
||||
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
|
||||
s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn);
|
||||
|
||||
// with a clip-space of [-w, w] ==> z' = -z
|
||||
// with a clip-space of [0, w] ==> z' = (w - z)/2
|
||||
s.clipControl = driver.getClipSpaceParams();
|
||||
FEngine const& engine, const CameraInfo& camera) noexcept {
|
||||
PerViewDescriptorSetUtils::prepareCamera(edit(transaction), engine, camera);
|
||||
// TODO: stereo values didn't used to be set
|
||||
}
|
||||
|
||||
void ShadowMapDescriptorSet::prepareLodBias(Transaction const& transaction, float const bias) noexcept {
|
||||
auto& s = edit(transaction);
|
||||
s.lodBias = bias;
|
||||
PerViewDescriptorSetUtils::prepareLodBias(edit(transaction), bias, 0);
|
||||
// TODO: check why derivativesScale was missing
|
||||
}
|
||||
|
||||
void ShadowMapDescriptorSet::prepareViewport(Transaction const& transaction,
|
||||
backend::Viewport const& viewport) noexcept {
|
||||
float2 const dimensions{ viewport.width, viewport.height };
|
||||
auto& s = edit(transaction);
|
||||
s.resolution = { dimensions, 1.0f / dimensions };
|
||||
s.logicalViewportScale = 1.0f;
|
||||
s.logicalViewportOffset = 0.0f;
|
||||
PerViewDescriptorSetUtils::prepareViewport(edit(transaction), viewport, viewport);
|
||||
// TODO: offset calculation is now different
|
||||
}
|
||||
|
||||
void ShadowMapDescriptorSet::prepareTime(Transaction const& transaction,
|
||||
FEngine const& engine, float4 const& userTime) noexcept {
|
||||
auto& s = edit(transaction);
|
||||
const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1'000'000'000;
|
||||
const float fraction = float(double(oneSecondRemainder) / 1'000'000'000.0);
|
||||
s.time = fraction;
|
||||
s.userTime = userTime;
|
||||
PerViewDescriptorSetUtils::prepareTime(edit(transaction), engine, userTime);
|
||||
}
|
||||
|
||||
void ShadowMapDescriptorSet::prepareMaterialGlobals(Transaction const& transaction,
|
||||
std::array<float4, 4> const& materialGlobals) noexcept {
|
||||
PerViewDescriptorSetUtils::prepareMaterialGlobals(edit(transaction), materialGlobals);
|
||||
}
|
||||
|
||||
void ShadowMapDescriptorSet::prepareShadowMapping(Transaction const& transaction,
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace filament {
|
||||
|
||||
struct CameraInfo;
|
||||
@@ -55,8 +57,10 @@ public:
|
||||
|
||||
void terminate(backend::DriverApi& driver);
|
||||
|
||||
// All UBO values that can affect user code must be set here
|
||||
|
||||
static void prepareCamera(Transaction const& transaction,
|
||||
backend::DriverApi& driver, const CameraInfo& camera) noexcept;
|
||||
FEngine const& engine, const CameraInfo& camera) noexcept;
|
||||
|
||||
static void prepareLodBias(Transaction const& transaction,
|
||||
float bias) noexcept;
|
||||
@@ -67,6 +71,9 @@ public:
|
||||
static void prepareTime(Transaction const& transaction,
|
||||
FEngine const& engine, math::float4 const& userTime) noexcept;
|
||||
|
||||
static void prepareMaterialGlobals(Transaction const& transaction,
|
||||
std::array<math::float4, 4> const& materialGlobals) noexcept;
|
||||
|
||||
static void prepareShadowMapping(Transaction const& transaction,
|
||||
bool highPrecision) noexcept;
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ void SsrPassDescriptorSet::prepareHistorySSR(FEngine const& engine, Handle<HwTex
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept {
|
||||
|
||||
mDescriptorSet.setSampler(engine.getPerViewDescriptorSetLayoutSsrVariant(),
|
||||
+PerViewBindingPoints::SSR, ssr, {
|
||||
+PerViewBindingPoints::SSR_HISTORY, ssr, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
|
||||
101
filament/src/ds/StructureDescriptorSet.cpp
Normal file
101
filament/src/ds/StructureDescriptorSet.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "StructureDescriptorSet.h"
|
||||
|
||||
#include "PerViewDescriptorSetUtils.h"
|
||||
|
||||
#include "details/Camera.h"
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
StructureDescriptorSet::StructureDescriptorSet() noexcept = default;
|
||||
|
||||
StructureDescriptorSet::~StructureDescriptorSet() noexcept {
|
||||
assert_invariant(!mDescriptorSet.getHandle());
|
||||
}
|
||||
|
||||
void StructureDescriptorSet::init(FEngine& engine) noexcept {
|
||||
|
||||
mUniforms.init(engine.getDriverApi());
|
||||
|
||||
mDescriptorSetLayout = &engine.getPerViewDescriptorSetLayoutDepthVariant();
|
||||
|
||||
// create the descriptor-set from the layout
|
||||
mDescriptorSet = DescriptorSet{
|
||||
"StructureDescriptorSet", *mDescriptorSetLayout };
|
||||
|
||||
// initialize the descriptor-set
|
||||
mDescriptorSet.setBuffer(*mDescriptorSetLayout, +PerViewBindingPoints::FRAME_UNIFORMS,
|
||||
mUniforms.getUboHandle(), 0, sizeof(PerViewUib));
|
||||
}
|
||||
|
||||
void StructureDescriptorSet::terminate(DriverApi& driver) {
|
||||
mDescriptorSet.terminate(driver);
|
||||
mUniforms.terminate(driver);
|
||||
}
|
||||
|
||||
void StructureDescriptorSet::commit(DriverApi& driver) noexcept {
|
||||
assert_invariant(mDescriptorSetLayout);
|
||||
driver.updateBufferObject(mUniforms.getUboHandle(),
|
||||
mUniforms.toBufferDescriptor(driver), 0);
|
||||
mDescriptorSet.commit(*mDescriptorSetLayout, driver);
|
||||
}
|
||||
|
||||
void StructureDescriptorSet::bind(DriverApi& driver) const noexcept {
|
||||
mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_VIEW);
|
||||
}
|
||||
|
||||
void StructureDescriptorSet::prepareViewport(
|
||||
backend::Viewport const& physicalViewport,
|
||||
backend::Viewport const& logicalViewport) noexcept {
|
||||
PerViewDescriptorSetUtils::prepareViewport(mUniforms.edit(), physicalViewport, logicalViewport);
|
||||
}
|
||||
|
||||
void StructureDescriptorSet::prepareCamera(FEngine const& engine,
|
||||
CameraInfo const& camera) noexcept {
|
||||
PerViewDescriptorSetUtils::prepareCamera(mUniforms.edit(), engine, camera);
|
||||
}
|
||||
|
||||
void StructureDescriptorSet::prepareLodBias(float bias, float2 const derivativesScale) noexcept {
|
||||
PerViewDescriptorSetUtils::prepareLodBias(mUniforms.edit(), bias, derivativesScale);
|
||||
}
|
||||
|
||||
void StructureDescriptorSet::prepareTime(FEngine const& engine, float4 const& userTime) noexcept {
|
||||
PerViewDescriptorSetUtils::prepareTime(mUniforms.edit(), engine, userTime);
|
||||
}
|
||||
|
||||
void StructureDescriptorSet::prepareMaterialGlobals(
|
||||
std::array<float4, 4> const& materialGlobals) noexcept {
|
||||
PerViewDescriptorSetUtils::prepareMaterialGlobals(mUniforms.edit(), materialGlobals);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
72
filament/src/ds/StructureDescriptorSet.h
Normal file
72
filament/src/ds/StructureDescriptorSet.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DescriptorSet.h"
|
||||
|
||||
#include "TypedUniformBuffer.h"
|
||||
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
struct Viewport;
|
||||
} // namespace backend
|
||||
|
||||
class FEngine;
|
||||
class DescriptorSetLayout;
|
||||
struct CameraInfo;
|
||||
|
||||
class StructureDescriptorSet {
|
||||
public:
|
||||
StructureDescriptorSet() noexcept;
|
||||
~StructureDescriptorSet() noexcept;
|
||||
|
||||
void init(FEngine& engine) noexcept;
|
||||
|
||||
void terminate(backend::DriverApi& driver);
|
||||
|
||||
void commit(backend::DriverApi& driver) noexcept;
|
||||
|
||||
void bind(backend::DriverApi& driver) const noexcept;
|
||||
|
||||
|
||||
// All UBO values that can affect user code must be set here
|
||||
|
||||
void prepareCamera(FEngine const& engine, const CameraInfo& camera) noexcept;
|
||||
|
||||
void prepareLodBias(float bias, math::float2 derivativesScale) noexcept;
|
||||
|
||||
void prepareViewport(const backend::Viewport& physicalViewport,
|
||||
const backend::Viewport& logicalViewport) noexcept;
|
||||
|
||||
void prepareTime(FEngine const& engine, math::float4 const& userTime) noexcept;
|
||||
|
||||
void prepareMaterialGlobals(std::array<math::float4, 4> const& materialGlobals) noexcept;
|
||||
|
||||
private:
|
||||
DescriptorSetLayout const* mDescriptorSetLayout = nullptr;
|
||||
DescriptorSet mDescriptorSet;
|
||||
TypedUniformBuffer<PerViewUib> mUniforms;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
@@ -21,7 +21,11 @@
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -31,7 +35,14 @@ template<typename T, size_t N = 1>
|
||||
class TypedUniformBuffer {
|
||||
public:
|
||||
|
||||
TypedUniformBuffer() noexcept = default;
|
||||
|
||||
explicit TypedUniformBuffer(backend::DriverApi& driver) noexcept {
|
||||
init(driver);
|
||||
}
|
||||
|
||||
void init(backend::DriverApi& driver) noexcept {
|
||||
assert_invariant(!mUboHandle);
|
||||
mUboHandle = driver.createBufferObject(
|
||||
mTypedBuffer.getSize(),
|
||||
backend::BufferObjectBinding::UNIFORM,
|
||||
@@ -39,7 +50,12 @@ public:
|
||||
}
|
||||
|
||||
void terminate(backend::DriverApi& driver) noexcept {
|
||||
driver.destroyBufferObject(mUboHandle);
|
||||
assert_invariant(mUboHandle);
|
||||
driver.destroyBufferObject(std::move(mUboHandle));
|
||||
}
|
||||
|
||||
~TypedUniformBuffer() noexcept {
|
||||
assert_invariant(!mUboHandle);
|
||||
}
|
||||
|
||||
TypedBuffer<T,N>& getTypedBuffer() noexcept {
|
||||
|
||||
@@ -54,7 +54,8 @@ enum class PerViewBindingPoints : uint8_t {
|
||||
IBL_DFG_LUT = 7, // user defined (128x128), RGB16F
|
||||
IBL_SPECULAR = 8, // user defined, user defined, CUBEMAP
|
||||
SSAO = 9, // variable, RGB8 {AO, [depth]}
|
||||
SSR = 10, // variable, RGB_11_11_10, mipmapped
|
||||
SSR = 10, // variable, 2d array, RGB_11_11_10, mipmapped
|
||||
SSR_HISTORY = 10, // variable, 2d texture, RGB_11_11_10
|
||||
FOG = 11 // variable, user defined, CUBEMAP
|
||||
};
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ static constexpr std::initializer_list<DescriptorSetLayoutBinding> depthVariantD
|
||||
static constexpr std::initializer_list<DescriptorSetLayoutBinding> ssrVariantDescriptorSetLayoutList = {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
||||
{ DescriptorType::SAMPLER_2D_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
|
||||
{ DescriptorType::SAMPLER_2D_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE, DescriptorFlags::UNFILTERABLE },
|
||||
{ DescriptorType::SAMPLER_2D_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR_HISTORY },
|
||||
};
|
||||
|
||||
// Used for generating the color pass (i.e. the main pass). This is in fact a template that gets
|
||||
@@ -83,7 +83,7 @@ static constexpr std::initializer_list<DescriptorSetLayoutBinding> perViewDescri
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::LIGHTS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::RECORD_BUFFER },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FROXEL_BUFFER },
|
||||
{ DescriptorType::SAMPLER_2D_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
|
||||
{ DescriptorType::SAMPLER_2D_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE, DescriptorFlags::UNFILTERABLE },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_DEPTH, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOW_MAP },
|
||||
{ DescriptorType::SAMPLER_2D_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::IBL_DFG_LUT },
|
||||
{ DescriptorType::SAMPLER_CUBE_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::IBL_SPECULAR },
|
||||
|
||||
@@ -45,8 +45,8 @@ def _compare_goldens(base_dir, comparison_dir, out_dir=None):
|
||||
output_fname = os.path.join(output_test_dir, "compare_results.json")
|
||||
results_meta = {
|
||||
'results': results,
|
||||
'base_dir': os.path.relpath(output_test_dir, base_test_dir),
|
||||
'comparison_dir': os.path.relpath(output_test_dir, comp_test_dir),
|
||||
'base_dir': os.path.relpath(base_test_dir, output_test_dir),
|
||||
'comparison_dir': os.path.relpath(comp_test_dir, output_test_dir)
|
||||
}
|
||||
with open(output_fname, 'w') as f:
|
||||
f.write(json.dumps(results_meta, indent=2))
|
||||
|
||||
@@ -95,8 +95,8 @@ def _download_github_artifacts(pr_number, github_token, output_dir= ".") -> None
|
||||
print("Consider checking GitHub Actions runs manually for this PR's branch on GitHub.")
|
||||
return None
|
||||
|
||||
# Filter for runs that completed successfully
|
||||
successful_runs = [run for run in workflow_runs if run.get("conclusion") == "success" and run.get("status") == "completed"]
|
||||
# Do not filter for runs that completed successfully
|
||||
successful_runs = sorted(workflow_runs, key=lambda run: run['id'], reverse=True)
|
||||
if not successful_runs:
|
||||
print(f"No *successful and completed* workflow runs found for PR #{pr_number} with commit SHA {commit_sha}. Exiting.")
|
||||
return None
|
||||
@@ -161,6 +161,10 @@ def _download_github_artifacts(pr_number, github_token, output_dir= ".") -> None
|
||||
print(f" Error: Downloaded file for '{artifact_name}' is not a valid zip file. Skipping extraction.")
|
||||
except Exception as e:
|
||||
print(f" An error occurred during extraction of '{artifact_name}': {e}")
|
||||
|
||||
# Once we find the lastest run with artifacts, we just quit
|
||||
if len(artifacts) > 0:
|
||||
break
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(f" An HTTP error occurred while fetching artifacts for run {run_id}: {e}")
|
||||
if e.response.status_code == 403:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,28 +19,37 @@
|
||||
#include <getopt/getopt.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace utils;
|
||||
|
||||
static const char* g_jsonMagicString = "__RESGEN__";
|
||||
static const char* g_packageName = "resources";
|
||||
static const char* g_deployDir = ".";
|
||||
static bool g_keepExtension = false;
|
||||
static bool g_appendNull = false;
|
||||
static bool g_generateC = false;
|
||||
static bool g_quietMode = false;
|
||||
static bool g_embedJson = false;
|
||||
// Holds all command-line configuration for the resource generator.
|
||||
struct AppConfig {
|
||||
const char* packageName = "resources";
|
||||
const char* deployDir = ".";
|
||||
bool keepExtension = false;
|
||||
bool appendNull = false;
|
||||
bool generateC = false;
|
||||
bool quietMode = false;
|
||||
bool embedJson = false;
|
||||
std::vector<Path> inputPaths;
|
||||
};
|
||||
|
||||
// A special marker used to identify where to insert the JSON summary blob.
|
||||
static const char* g_jsonMagicString = "__RESGEN__";
|
||||
|
||||
// Defines the help message text displayed to the user.
|
||||
static const char* USAGE = R"TXT(
|
||||
RESGEN aggregates a sequence of binary blobs, each of which becomes a "resource" whose id
|
||||
is the basename of the input file. It produces the following set of files:
|
||||
@@ -82,6 +91,7 @@ Examples:
|
||||
TEXTURES_BEACH_DATA, TEXTURES_BEACH_SIZE
|
||||
)TXT";
|
||||
|
||||
// Template for the Apple-specific assembly file.
|
||||
static const char* APPLE_ASM_TEMPLATE = R"ASM(
|
||||
.global _{RESOURCES}PACKAGE
|
||||
.section __TEXT,__const
|
||||
@@ -89,6 +99,7 @@ _{RESOURCES}PACKAGE:
|
||||
.incbin "{resources}.bin"
|
||||
)ASM";
|
||||
|
||||
// Template for the standard assembly file.
|
||||
static const char* ASM_TEMPLATE = R"ASM(
|
||||
.global {RESOURCES}PACKAGE
|
||||
.section .rodata
|
||||
@@ -96,287 +107,304 @@ static const char* ASM_TEMPLATE = R"ASM(
|
||||
.incbin "{resources}.bin"
|
||||
)ASM";
|
||||
|
||||
// Prints the usage string, replacing the placeholder with the actual executable name.
|
||||
static void printUsage(const char* name) {
|
||||
std::string execName(Path(name).getName());
|
||||
std::string const execName(Path(name).getName());
|
||||
const std::string from("RESGEN");
|
||||
std::string usage(USAGE);
|
||||
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
|
||||
std::size_t pos = 0;
|
||||
while ((pos = usage.find(from, pos)) != std::string::npos) {
|
||||
usage.replace(pos, from.length(), execName);
|
||||
pos += execName.length();
|
||||
}
|
||||
puts(usage.c_str());
|
||||
std::cout << usage;
|
||||
}
|
||||
|
||||
// Prints the license text to the console.
|
||||
static void license() {
|
||||
static const char *license[] = {
|
||||
#include "licenses/licenses.inc"
|
||||
nullptr
|
||||
};
|
||||
|
||||
const char **p = &license[0];
|
||||
while (*p)
|
||||
std::cout << *p++ << std::endl;
|
||||
for (const char **p = &license[0]; *p; ++p) {
|
||||
std::cout << *p << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
static int handleArguments(int argc, char* argv[]) {
|
||||
// A helper function to replace all occurrences of a substring within a string.
|
||||
static std::string& replaceAll(std::string& context, const std::string& from, const std::string& to) {
|
||||
std::size_t pos = 0;
|
||||
while ((pos = context.find(from, pos)) != std::string::npos) {
|
||||
context.replace(pos, from.length(), to);
|
||||
pos += to.length();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
// Parses command-line arguments using getopt and populates the AppConfig struct.
|
||||
static AppConfig handleArguments(int const argc, char* argv[]) {
|
||||
static constexpr const char* OPTSTR = "hLp:x:ktcqj";
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "license", no_argument, 0, 'L' },
|
||||
{ "package", required_argument, 0, 'p' },
|
||||
{ "deploy", required_argument, 0, 'x' },
|
||||
{ "keep", no_argument, 0, 'k' },
|
||||
{ "text", no_argument, 0, 't' },
|
||||
{ "cfile", no_argument, 0, 'c' },
|
||||
{ "quiet", no_argument, 0, 'q' },
|
||||
{ "json", no_argument, 0, 'j' },
|
||||
{ 0, 0, 0, 0 } // termination of the option list
|
||||
static const option OPTIONS[] = {
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
{ "license", no_argument, nullptr, 'L' },
|
||||
{ "package", required_argument, nullptr, 'p' },
|
||||
{ "deploy", required_argument, nullptr, 'x' },
|
||||
{ "keep", no_argument, nullptr, 'k' },
|
||||
{ "text", no_argument, nullptr, 't' },
|
||||
{ "cfile", no_argument, nullptr, 'c' },
|
||||
{ "quiet", no_argument, nullptr, 'q' },
|
||||
{ "json", no_argument, nullptr, 'j' },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
AppConfig config;
|
||||
int opt;
|
||||
int optionIndex = 0;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &optionIndex)) >= 0) {
|
||||
std::string arg(optarg ? optarg : "");
|
||||
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, nullptr)) >= 0) {
|
||||
switch (opt) {
|
||||
default:
|
||||
case 'h':
|
||||
printUsage(argv[0]);
|
||||
exit(0);
|
||||
std::exit(0);
|
||||
case 'L':
|
||||
license();
|
||||
exit(0);
|
||||
std::exit(0);
|
||||
case 'p':
|
||||
g_packageName = optarg;
|
||||
config.packageName = optarg;
|
||||
break;
|
||||
case 'x':
|
||||
g_deployDir = optarg;
|
||||
config.deployDir = optarg;
|
||||
break;
|
||||
case 'k':
|
||||
g_keepExtension = true;
|
||||
config.keepExtension = true;
|
||||
break;
|
||||
case 't':
|
||||
g_appendNull = true;
|
||||
config.appendNull = true;
|
||||
break;
|
||||
case 'c':
|
||||
g_generateC = true;
|
||||
config.generateC = true;
|
||||
break;
|
||||
case 'q':
|
||||
g_quietMode = true;
|
||||
config.quietMode = true;
|
||||
break;
|
||||
case 'j':
|
||||
g_embedJson = true;
|
||||
config.embedJson = true;
|
||||
break;
|
||||
default:
|
||||
printUsage(argv[0]);
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return optind;
|
||||
// Treat remaining arguments as input file paths.
|
||||
for (int i = optind; i < argc; ++i) {
|
||||
config.inputPaths.emplace_back(argv[i]);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Opens a file for writing, printing an error and exiting on failure.
|
||||
static std::ofstream openOutputFile(const Path& path, std::ios_base::openmode const mode = std::ios_base::out) {
|
||||
std::ofstream stream(path.getPath(), mode);
|
||||
if (!stream) {
|
||||
std::cerr << "Unable to open " << path << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Reads an entire binary file into a vector of bytes.
|
||||
static std::vector<std::uint8_t> readFile(const Path& path) {
|
||||
std::ifstream inStream(path.getPath(), std::ios::binary);
|
||||
if (!inStream) {
|
||||
std::cerr << "Unable to open " << path << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
return std::vector<std::uint8_t>((std::istreambuf_iterator<char>(inStream)),
|
||||
(std::istreambuf_iterator<char>()));
|
||||
}
|
||||
|
||||
// Writes the generated header file, but only if its content has changed.
|
||||
// This helps avoid unnecessary recompilation in build systems.
|
||||
static void writeHeaderIfChanged(const Path& path, const std::string& newContents) {
|
||||
std::ifstream headerInStream(path.getPath(), std::ifstream::ate);
|
||||
if (headerInStream) {
|
||||
long const fileSize = headerInStream.tellg();
|
||||
if (fileSize == newContents.size()) {
|
||||
std::vector<char> previous(fileSize);
|
||||
headerInStream.seekg(0);
|
||||
headerInStream.read(previous.data(), fileSize);
|
||||
if (0 == memcmp(previous.data(), newContents.c_str(), fileSize)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::ofstream headerOutStream = openOutputFile(path);
|
||||
headerOutStream << newContents;
|
||||
}
|
||||
|
||||
// Writes a resource's data to a C-style char array (similar to 'xxd -i').
|
||||
static void writeXxdEntry(std::ofstream& xxdStream, const std::string& resourceName,
|
||||
const std::vector<std::uint8_t>& content) {
|
||||
xxdStream << "// " << resourceName << "\n";
|
||||
xxdStream << std::setfill('0') << std::hex;
|
||||
for (std::size_t i = 0; i < content.size(); i++) {
|
||||
if (i > 0 && i % 20 == 0) {
|
||||
xxdStream << "\n";
|
||||
}
|
||||
xxdStream << "0x" << std::setw(2) << int(content[i]) << ", ";
|
||||
}
|
||||
if (!content.empty() && content.size() % 20 != 0) {
|
||||
xxdStream << "\n";
|
||||
}
|
||||
xxdStream << "\n";
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
const int optionIndex = handleArguments(argc, argv);
|
||||
const int numArgs = argc - optionIndex;
|
||||
if (numArgs < 1) {
|
||||
// Parse arguments and check for input files.
|
||||
const AppConfig config = handleArguments(argc, argv);
|
||||
if (config.inputPaths.empty()) {
|
||||
printUsage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vector<Path> inputPaths;
|
||||
for (int argIndex = optionIndex; argIndex < argc; ++argIndex) {
|
||||
inputPaths.emplace_back(argv[argIndex]);
|
||||
}
|
||||
if (g_embedJson) {
|
||||
inputPaths.push_back(g_jsonMagicString);
|
||||
// If JSON embedding is enabled, add a placeholder to the input list.
|
||||
std::vector<Path> inputPaths = config.inputPaths;
|
||||
if (config.embedJson) {
|
||||
inputPaths.emplace_back(g_jsonMagicString);
|
||||
}
|
||||
|
||||
std::string packageFile = g_packageName;
|
||||
std::string packagePrefix = std::string(g_packageName) + "_";
|
||||
transform(packagePrefix.begin(), packagePrefix.end(), packagePrefix.begin(), ::toupper);
|
||||
std::string package = packagePrefix + "PACKAGE";
|
||||
// Generate names and symbols based on the package name.
|
||||
const std::string packageFile = config.packageName;
|
||||
std::string packagePrefix = std::string(config.packageName) + "_";
|
||||
std::transform(packagePrefix.begin(), packagePrefix.end(), packagePrefix.begin(), [](unsigned char c) { return std::toupper(c); });
|
||||
const std::string packageSymbol = packagePrefix + "PACKAGE";
|
||||
|
||||
const Path deployDir(g_deployDir);
|
||||
// Create the deployment directory if it doesn't exist.
|
||||
const Path deployDir(config.deployDir);
|
||||
if (!deployDir.exists()) {
|
||||
deployDir.mkdirRecursive();
|
||||
}
|
||||
|
||||
// Define all output file paths.
|
||||
const Path appleAsmPath(deployDir + (packageFile + ".apple.S"));
|
||||
const Path asmPath(deployDir + (packageFile + ".S"));
|
||||
const Path binPath(deployDir + (packageFile + ".bin"));
|
||||
const Path headerPath(deployDir + (packageFile + ".h"));
|
||||
const Path xxdPath(deployDir + (packageFile + ".c"));
|
||||
|
||||
// In the assembly language templates, replace {RESOURCES} with packagePrefix and replace
|
||||
// {resources} with packageFile.
|
||||
const std::string k1("{RESOURCES}");
|
||||
const std::string k2("{resources}");
|
||||
// Prepare assembly file content by replacing placeholders.
|
||||
std::string aasmstr(APPLE_ASM_TEMPLATE);
|
||||
replaceAll(aasmstr, "{RESOURCES}", packagePrefix);
|
||||
replaceAll(aasmstr, "{resources}", packageFile);
|
||||
|
||||
std::string asmstr(ASM_TEMPLATE);
|
||||
for (size_t pos = aasmstr.find(k1); pos != std::string::npos; pos = aasmstr.find(k1, pos))
|
||||
aasmstr.replace(pos, k1.length(), packagePrefix);
|
||||
for (size_t pos = aasmstr.find(k2); pos != std::string::npos; pos = aasmstr.find(k2, pos))
|
||||
aasmstr.replace(pos, k2.length(), packageFile);
|
||||
for (size_t pos = asmstr.find(k1); pos != std::string::npos; pos = asmstr.find(k1, pos))
|
||||
asmstr.replace(pos, k1.length(), packagePrefix);
|
||||
for (size_t pos = asmstr.find(k2); pos != std::string::npos; pos = asmstr.find(k2, pos))
|
||||
asmstr.replace(pos, k2.length(), packageFile);
|
||||
replaceAll(asmstr, "{RESOURCES}", packagePrefix);
|
||||
replaceAll(asmstr, "{resources}", packageFile);
|
||||
|
||||
// Open the Apple-friendly assembly language file.
|
||||
ofstream appleAsmStream(appleAsmPath.getPath());
|
||||
if (!appleAsmStream) {
|
||||
cerr << "Unable to open " << appleAsmPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
// Open all output file streams.
|
||||
auto appleAsmStream = openOutputFile(appleAsmPath);
|
||||
auto asmStream = openOutputFile(asmPath);
|
||||
auto binStream = openOutputFile(binPath, std::ios::binary);
|
||||
|
||||
// Open the non-Apple assembly language file.
|
||||
ofstream asmStream(asmPath.getPath());
|
||||
if (!asmStream) {
|
||||
cerr << "Unable to open " << asmPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
// Begin constructing the C header file content.
|
||||
std::ostringstream headerStream;
|
||||
headerStream << "#ifndef " << packagePrefix << "H_\n"
|
||||
<< "#define " << packagePrefix << "H_\n\n"
|
||||
<< "#include <stdint.h>\n\n"
|
||||
<< "extern \"C\" {\n"
|
||||
<< " extern const uint8_t " << packageSymbol << "[];\n";
|
||||
|
||||
// Open the bin file for writing.
|
||||
ofstream binStream(binPath.getPath(), ios::binary);
|
||||
if (!binStream) {
|
||||
cerr << "Unable to open " << binPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
// String streams to accumulate header macros and the JSON summary.
|
||||
std::ostringstream headerMacros;
|
||||
std::ostringstream jsonStream;
|
||||
|
||||
// Open the header file stream for writing.
|
||||
ostringstream headerStream;
|
||||
headerStream << "#ifndef " << packagePrefix << "H_" << endl
|
||||
<< "#define " << packagePrefix << "H_" << endl << endl
|
||||
<< "#include <stdint.h>\n" << endl
|
||||
<< "extern \"C\" {" << endl
|
||||
<< " extern const uint8_t " << package << "[];" << endl;
|
||||
|
||||
ostringstream headerMacros;
|
||||
ostringstream appleDataAsmStream;
|
||||
ostringstream dataAsmStream;
|
||||
ostringstream jsonStream;
|
||||
|
||||
// Open the generated C file for writing.
|
||||
ofstream xxdStream;
|
||||
if (g_generateC) {
|
||||
xxdStream = ofstream(xxdPath.getPath());
|
||||
if (!xxdStream) {
|
||||
cerr << "Unable to open " << xxdPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
// If generating a C file, open the stream and write the initial boilerplate.
|
||||
std::ofstream xxdStream;
|
||||
if (config.generateC) {
|
||||
xxdStream = openOutputFile(xxdPath);
|
||||
xxdStream << "#include <stdint.h>\n"
|
||||
<< "const uint8_t " << package << "[] = {\n";
|
||||
<< "const uint8_t " << packageSymbol << "[] = {\n";
|
||||
}
|
||||
|
||||
// Consume each input file and write it back out into the various output streams.
|
||||
// Process each input file to build the resource collection.
|
||||
jsonStream << "{";
|
||||
size_t offset = 0;
|
||||
std::size_t offset = 0;
|
||||
for (const auto& inPath : inputPaths) {
|
||||
vector<uint8_t> content;
|
||||
std::vector<std::uint8_t> content;
|
||||
if (inPath != g_jsonMagicString) {
|
||||
ifstream inStream(inPath.getPath(), ios::binary);
|
||||
if (!inStream) {
|
||||
cerr << "Unable to open " << inPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
content = vector<uint8_t>((istreambuf_iterator<char>(inStream)), {});
|
||||
// For a regular file, read its binary content.
|
||||
content = readFile(inPath);
|
||||
} else {
|
||||
// To finalize the JSON string, we replace the trailing comma with an end bracket and
|
||||
// prefix it with the magic identifier and string size.
|
||||
// For the JSON placeholder, finalize and embed the JSON summary blob.
|
||||
// The blob is formatted as: __RESGEN__\0<size>\0{...json...}
|
||||
std::string jsonString = jsonStream.str();
|
||||
jsonString[jsonString.size()-1] = '}';
|
||||
ostringstream jsonBlob;
|
||||
jsonBlob << g_jsonMagicString << "\0";
|
||||
jsonBlob << jsonString.size() << "\0";
|
||||
jsonString[jsonString.size() - 1] = '}';
|
||||
std::ostringstream jsonBlob;
|
||||
jsonBlob << g_jsonMagicString << '\0';
|
||||
jsonBlob << jsonString.size() << '\0';
|
||||
jsonBlob << jsonString;
|
||||
jsonString = jsonBlob.str();
|
||||
uint8_t const* jsonPtr = (uint8_t const*) jsonString.c_str();
|
||||
content = vector<uint8_t>(jsonPtr, jsonPtr + jsonBlob.str().size());
|
||||
const auto* jsonPtr = reinterpret_cast<const std::uint8_t*>(jsonString.c_str());
|
||||
content.assign(jsonPtr, jsonPtr + jsonBlob.str().size());
|
||||
}
|
||||
if (g_appendNull) {
|
||||
|
||||
// Optionally append a null terminator, useful for text resources.
|
||||
if (config.appendNull) {
|
||||
content.push_back(0);
|
||||
}
|
||||
|
||||
// Formulate the resource name and the prefixed resource name.
|
||||
std::string rname = g_keepExtension ? inPath.getName() : inPath.getNameWithoutExtension();
|
||||
replace(rname.begin(), rname.end(), '.', '_');
|
||||
transform(rname.begin(), rname.end(), rname.begin(), ::toupper);
|
||||
// Generate the resource's symbol name from its file name.
|
||||
std::string rname = config.keepExtension ? inPath.getName() : inPath.getNameWithoutExtension();
|
||||
replaceAll(rname, ".", "_");
|
||||
std::transform(rname.begin(), rname.end(), rname.begin(), [](unsigned char c) { return std::toupper(c); });
|
||||
const std::string prname = packagePrefix + rname;
|
||||
|
||||
// Write the binary blob into the bin file.
|
||||
binStream.write((const char*) content.data(), content.size());
|
||||
// Write the resource content to the aggregate binary file.
|
||||
binStream.write(reinterpret_cast<const char*>(content.data()), content.size());
|
||||
|
||||
// Write the offsets and sizes.
|
||||
headerMacros
|
||||
<< "#define " << prname << "_OFFSET " << offset << "\n"
|
||||
<< "#define " << prname << "_SIZE " << content.size() << "\n"
|
||||
<< "#define " << prname << "_DATA (" << package << " + " << prname << "_OFFSET)\n\n";
|
||||
// Generate C preprocessor macros for the resource's offset, size, and data pointer.
|
||||
headerMacros << "#define " << prname << "_OFFSET " << offset << "\n"
|
||||
<< "#define " << prname << "_SIZE " << content.size() << "\n"
|
||||
<< "#define " << prname << "_DATA (" << packageSymbol << " + " << prname << "_OFFSET)\n\n";
|
||||
|
||||
// Write the xxd-style ASCII array, followed by a blank line.
|
||||
if (g_generateC) {
|
||||
xxdStream << "// " << rname << "\n";
|
||||
xxdStream << setfill('0') << hex;
|
||||
size_t i = 0;
|
||||
for (; i < content.size(); i++) {
|
||||
if (i > 0 && i % 20 == 0) {
|
||||
xxdStream << "\n";
|
||||
}
|
||||
xxdStream << "0x" << setw(2) << (int) content[i] << ", ";
|
||||
}
|
||||
if (i % 20 != 0) xxdStream << "\n";
|
||||
xxdStream << "\n";
|
||||
// If enabled, write the content to the C source file.
|
||||
if (config.generateC) {
|
||||
writeXxdEntry(xxdStream, rname, content);
|
||||
}
|
||||
|
||||
// Add an entry to the JSON summary and update the running offset.
|
||||
jsonStream << "\"" << rname << "\":" << content.size() << ",";
|
||||
offset += content.size();
|
||||
}
|
||||
|
||||
// Finalize the header file content.
|
||||
headerStream << "}\n\n";
|
||||
headerStream << headerMacros.str();
|
||||
headerStream << "#endif\n";
|
||||
|
||||
// To optimize builds, avoid overwriting the header file if nothing has changed.
|
||||
bool headerIsDirty = true;
|
||||
ifstream headerInStream(headerPath.getPath(), std::ifstream::ate);
|
||||
string headerContents = headerStream.str();
|
||||
if (headerInStream) {
|
||||
long fileSize = static_cast<long>(headerInStream.tellg());
|
||||
if (fileSize == headerContents.size()) {
|
||||
vector<char> previous(fileSize);
|
||||
headerInStream.seekg(0);
|
||||
headerInStream.read(previous.data(), fileSize);
|
||||
headerIsDirty = 0 != memcmp(previous.data(), headerContents.c_str(), fileSize);
|
||||
}
|
||||
// Write the header and assembly files.
|
||||
writeHeaderIfChanged(headerPath, headerStream.str());
|
||||
asmStream << asmstr << std::endl;
|
||||
appleAsmStream << aasmstr << std::endl;
|
||||
|
||||
// Report generated files to the console unless in quiet mode.
|
||||
if (!config.quietMode) {
|
||||
std::cout << "Generated files: " << headerPath << " " << asmPath << " " << appleAsmPath << " "
|
||||
<< binPath;
|
||||
}
|
||||
|
||||
if (headerIsDirty) {
|
||||
ofstream headerOutStream(headerPath.getPath());
|
||||
if (!headerOutStream) {
|
||||
cerr << "Unable to open " << headerPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
headerOutStream << headerContents;
|
||||
}
|
||||
|
||||
asmStream << asmstr << dataAsmStream.str() << endl;
|
||||
asmStream.close();
|
||||
|
||||
appleAsmStream << aasmstr << appleDataAsmStream.str() << endl;
|
||||
appleAsmStream.close();
|
||||
|
||||
if (!g_quietMode) {
|
||||
cout << "Generated files: "
|
||||
<< headerPath << " "
|
||||
<< asmPath << " "
|
||||
<< appleAsmPath << " "
|
||||
<< binPath;
|
||||
}
|
||||
|
||||
if (g_generateC) {
|
||||
// Finalize the C file and report it.
|
||||
if (config.generateC) {
|
||||
xxdStream << "};\n\n";
|
||||
if (!g_quietMode) {
|
||||
cout << " " << xxdPath;
|
||||
if (!config.quietMode) {
|
||||
std::cout << " " << xxdPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_quietMode) {
|
||||
cout << endl;
|
||||
if (!config.quietMode) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user