You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
463 lines
16 KiB
463 lines
16 KiB
/****************************************************************************
|
|
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
|
|
|
http://www.cocos.com
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include <boost/align/align_up.hpp>
|
|
#include <cstring>
|
|
#include "application/ApplicationManager.h"
|
|
#include "base/Log.h"
|
|
#include "base/threading/MessageQueue.h"
|
|
#include "base/threading/ThreadSafeLinearAllocator.h"
|
|
#include "platform/interfaces/modules/IXRInterface.h"
|
|
|
|
#include "BufferAgent.h"
|
|
#include "CommandBufferAgent.h"
|
|
#include "DescriptorSetAgent.h"
|
|
#include "DescriptorSetLayoutAgent.h"
|
|
#include "DeviceAgent.h"
|
|
#include "FramebufferAgent.h"
|
|
#include "InputAssemblerAgent.h"
|
|
#include "PipelineLayoutAgent.h"
|
|
#include "PipelineStateAgent.h"
|
|
#include "QueryPoolAgent.h"
|
|
#include "QueueAgent.h"
|
|
#include "RenderPassAgent.h"
|
|
#include "ShaderAgent.h"
|
|
#include "SwapchainAgent.h"
|
|
#include "TextureAgent.h"
|
|
|
|
namespace cc {
|
|
namespace gfx {
|
|
|
|
DeviceAgent *DeviceAgent::instance = nullptr;
|
|
|
|
DeviceAgent *DeviceAgent::getInstance() {
|
|
return DeviceAgent::instance;
|
|
}
|
|
|
|
DeviceAgent::DeviceAgent(Device *device) : Agent(device) {
|
|
DeviceAgent::instance = this;
|
|
}
|
|
|
|
DeviceAgent::~DeviceAgent() {
|
|
CC_SAFE_DELETE(_actor);
|
|
DeviceAgent::instance = nullptr;
|
|
}
|
|
|
|
bool DeviceAgent::doInit(const DeviceInfo &info) {
|
|
if (!_actor->initialize(info)) {
|
|
return false;
|
|
}
|
|
_xr = CC_GET_XR_INTERFACE();
|
|
_api = _actor->getGfxAPI();
|
|
_deviceName = _actor->getDeviceName();
|
|
_queue = ccnew QueueAgent(_actor->getQueue());
|
|
_queryPool = ccnew QueryPoolAgent(_actor->getQueryPool());
|
|
_cmdBuff = ccnew CommandBufferAgent(_actor->getCommandBuffer());
|
|
_renderer = _actor->getRenderer();
|
|
_vendor = _actor->getVendor();
|
|
_caps = _actor->_caps;
|
|
memcpy(_features.data(), _actor->_features.data(), static_cast<uint32_t>(Feature::COUNT) * sizeof(bool));
|
|
memcpy(_formatFeatures.data(), _actor->_formatFeatures.data(), static_cast<uint32_t>(Format::COUNT) * sizeof(FormatFeatureBit));
|
|
|
|
_mainMessageQueue = ccnew MessageQueue;
|
|
|
|
static_cast<CommandBufferAgent *>(_cmdBuff)->_queue = _queue;
|
|
static_cast<CommandBufferAgent *>(_cmdBuff)->initAgent();
|
|
|
|
setMultithreaded(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
void DeviceAgent::doDestroy() {
|
|
if (!_mainMessageQueue) {
|
|
_actor->destroy();
|
|
} else {
|
|
ENQUEUE_MESSAGE_1(
|
|
_mainMessageQueue, DeviceDestroy,
|
|
actor, _actor,
|
|
{
|
|
actor->destroy();
|
|
});
|
|
}
|
|
|
|
if (_cmdBuff) {
|
|
static_cast<CommandBufferAgent *>(_cmdBuff)->destroyAgent();
|
|
static_cast<CommandBufferAgent *>(_cmdBuff)->_actor = nullptr;
|
|
delete _cmdBuff;
|
|
_cmdBuff = nullptr;
|
|
}
|
|
if (_queryPool) {
|
|
static_cast<QueryPoolAgent *>(_queryPool)->_actor = nullptr;
|
|
delete _queryPool;
|
|
_queryPool = nullptr;
|
|
}
|
|
if (_queue) {
|
|
static_cast<QueueAgent *>(_queue)->_actor = nullptr;
|
|
delete _queue;
|
|
_queue = nullptr;
|
|
}
|
|
|
|
if (_mainMessageQueue) {
|
|
_mainMessageQueue->terminateConsumerThread();
|
|
|
|
delete _mainMessageQueue;
|
|
_mainMessageQueue = nullptr;
|
|
}
|
|
}
|
|
|
|
void DeviceAgent::acquire(Swapchain *const *swapchains, uint32_t count) {
|
|
auto *actorSwapchains = _mainMessageQueue->allocate<Swapchain *>(count);
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
actorSwapchains[i] = static_cast<SwapchainAgent *>(swapchains[i])->getActor();
|
|
}
|
|
|
|
ENQUEUE_MESSAGE_4(
|
|
_mainMessageQueue, DevicePresent,
|
|
device, this,
|
|
actor, _actor,
|
|
swapchains, actorSwapchains,
|
|
count, count,
|
|
{
|
|
if (device->_onAcquire) device->_onAcquire->execute();
|
|
actor->acquire(swapchains, count);
|
|
});
|
|
}
|
|
|
|
void DeviceAgent::present() {
|
|
if (_xr) {
|
|
ENQUEUE_MESSAGE_1(
|
|
_mainMessageQueue, DevicePresent,
|
|
actor, _actor,
|
|
{
|
|
actor->present();
|
|
});
|
|
} else {
|
|
ENQUEUE_MESSAGE_2(
|
|
_mainMessageQueue, DevicePresent,
|
|
actor, _actor,
|
|
frameBoundarySemaphore, &_frameBoundarySemaphore,
|
|
{
|
|
actor->present();
|
|
frameBoundarySemaphore->signal();
|
|
});
|
|
|
|
MessageQueue::freeChunksInFreeQueue(_mainMessageQueue);
|
|
_mainMessageQueue->finishWriting();
|
|
_currentIndex = (_currentIndex + 1) % MAX_FRAME_INDEX;
|
|
_frameBoundarySemaphore.wait();
|
|
}
|
|
}
|
|
|
|
void DeviceAgent::setMultithreaded(bool multithreaded) {
|
|
if (multithreaded == _multithreaded) return;
|
|
_multithreaded = multithreaded;
|
|
|
|
if (multithreaded) {
|
|
_mainMessageQueue->setImmediateMode(false);
|
|
_actor->bindContext(false);
|
|
_mainMessageQueue->runConsumerThread();
|
|
ENQUEUE_MESSAGE_1(
|
|
_mainMessageQueue, DeviceMakeCurrentTrue,
|
|
actor, _actor,
|
|
{
|
|
actor->bindContext(true);
|
|
CC_LOG_INFO("Device thread detached.");
|
|
});
|
|
for (CommandBufferAgent *cmdBuff : _cmdBuffRefs) {
|
|
cmdBuff->_messageQueue->setImmediateMode(false);
|
|
}
|
|
} else {
|
|
ENQUEUE_MESSAGE_1(
|
|
_mainMessageQueue, DeviceMakeCurrentFalse,
|
|
actor, _actor,
|
|
{
|
|
actor->bindContext(false);
|
|
});
|
|
_mainMessageQueue->terminateConsumerThread();
|
|
_mainMessageQueue->setImmediateMode(true);
|
|
_actor->bindContext(true);
|
|
for (CommandBufferAgent *cmdBuff : _cmdBuffRefs) {
|
|
cmdBuff->_messageQueue->setImmediateMode(true);
|
|
}
|
|
CC_LOG_INFO("Device thread joined.");
|
|
}
|
|
}
|
|
|
|
CommandBuffer *DeviceAgent::createCommandBuffer(const CommandBufferInfo &info, bool /*hasAgent*/) {
|
|
CommandBuffer *actor = _actor->createCommandBuffer(info, true);
|
|
return ccnew CommandBufferAgent(actor);
|
|
}
|
|
|
|
Queue *DeviceAgent::createQueue() {
|
|
Queue *actor = _actor->createQueue();
|
|
return ccnew QueueAgent(actor);
|
|
}
|
|
|
|
QueryPool *DeviceAgent::createQueryPool() {
|
|
QueryPool *actor = _actor->createQueryPool();
|
|
return ccnew QueryPoolAgent(actor);
|
|
}
|
|
|
|
Swapchain *DeviceAgent::createSwapchain() {
|
|
Swapchain *actor = _actor->createSwapchain();
|
|
return ccnew SwapchainAgent(actor);
|
|
}
|
|
|
|
Buffer *DeviceAgent::createBuffer() {
|
|
Buffer *actor = _actor->createBuffer();
|
|
return ccnew BufferAgent(actor);
|
|
}
|
|
|
|
Texture *DeviceAgent::createTexture() {
|
|
Texture *actor = _actor->createTexture();
|
|
return ccnew TextureAgent(actor);
|
|
}
|
|
|
|
Shader *DeviceAgent::createShader() {
|
|
Shader *actor = _actor->createShader();
|
|
return ccnew ShaderAgent(actor);
|
|
}
|
|
|
|
InputAssembler *DeviceAgent::createInputAssembler() {
|
|
InputAssembler *actor = _actor->createInputAssembler();
|
|
return ccnew InputAssemblerAgent(actor);
|
|
}
|
|
|
|
RenderPass *DeviceAgent::createRenderPass() {
|
|
RenderPass *actor = _actor->createRenderPass();
|
|
return ccnew RenderPassAgent(actor);
|
|
}
|
|
|
|
Framebuffer *DeviceAgent::createFramebuffer() {
|
|
Framebuffer *actor = _actor->createFramebuffer();
|
|
return ccnew FramebufferAgent(actor);
|
|
}
|
|
|
|
DescriptorSet *DeviceAgent::createDescriptorSet() {
|
|
DescriptorSet *actor = _actor->createDescriptorSet();
|
|
return ccnew DescriptorSetAgent(actor);
|
|
}
|
|
|
|
DescriptorSetLayout *DeviceAgent::createDescriptorSetLayout() {
|
|
DescriptorSetLayout *actor = _actor->createDescriptorSetLayout();
|
|
return ccnew DescriptorSetLayoutAgent(actor);
|
|
}
|
|
|
|
PipelineLayout *DeviceAgent::createPipelineLayout() {
|
|
PipelineLayout *actor = _actor->createPipelineLayout();
|
|
return ccnew PipelineLayoutAgent(actor);
|
|
}
|
|
|
|
PipelineState *DeviceAgent::createPipelineState() {
|
|
PipelineState *actor = _actor->createPipelineState();
|
|
return ccnew PipelineStateAgent(actor);
|
|
}
|
|
|
|
Sampler *DeviceAgent::getSampler(const SamplerInfo &info) {
|
|
return _actor->getSampler(info);
|
|
}
|
|
|
|
GeneralBarrier *DeviceAgent::getGeneralBarrier(const GeneralBarrierInfo &info) {
|
|
return _actor->getGeneralBarrier(info);
|
|
}
|
|
|
|
TextureBarrier *DeviceAgent::getTextureBarrier(const TextureBarrierInfo &info) {
|
|
return _actor->getTextureBarrier(info);
|
|
}
|
|
|
|
BufferBarrier *DeviceAgent::getBufferBarrier(const BufferBarrierInfo &info) {
|
|
return _actor->getBufferBarrier(info);
|
|
}
|
|
|
|
template <typename T>
|
|
void doBufferTextureCopy(const uint8_t *const *buffers, Texture *texture, const BufferTextureCopy *regions, uint32_t count, MessageQueue *mq, T *actor) {
|
|
uint32_t bufferCount = 0U;
|
|
for (uint32_t i = 0U; i < count; i++) {
|
|
bufferCount += regions[i].texSubres.layerCount;
|
|
}
|
|
|
|
Format format = texture->getFormat();
|
|
constexpr uint32_t alignment = 16;
|
|
|
|
size_t totalSize = boost::alignment::align_up(sizeof(BufferTextureCopy) * count + sizeof(uint8_t *) * bufferCount, alignment);
|
|
for (uint32_t i = 0U; i < count; i++) {
|
|
const BufferTextureCopy ®ion = regions[i];
|
|
|
|
uint32_t size = formatSize(texture->getFormat(), region.texExtent.width, region.texExtent.height, region.texExtent.depth);
|
|
totalSize += boost::alignment::align_up(size, alignment) * region.texSubres.layerCount;
|
|
}
|
|
|
|
auto *allocator = ccnew ThreadSafeLinearAllocator(totalSize, alignment);
|
|
|
|
auto *actorRegions = allocator->allocate<BufferTextureCopy>(count);
|
|
memcpy(actorRegions, regions, count * sizeof(BufferTextureCopy));
|
|
|
|
const auto **actorBuffers = allocator->allocate<const uint8_t *>(bufferCount);
|
|
const auto blockHeight = formatAlignment(format).second;
|
|
for (uint32_t i = 0U, n = 0U; i < count; i++) {
|
|
const BufferTextureCopy ®ion = regions[i];
|
|
uint32_t width = region.texExtent.width;
|
|
uint32_t height = region.texExtent.height;
|
|
uint32_t depth = region.texExtent.depth;
|
|
|
|
uint32_t rowStride = region.buffStride > 0 ? region.buffStride : region.texExtent.width;
|
|
uint32_t heightStride = region.buffTexHeight > 0 ? region.buffTexHeight : region.texExtent.height;
|
|
|
|
uint32_t rowStrideSize = formatSize(format, rowStride, 1, 1);
|
|
uint32_t sliceStrideSize = formatSize(format, rowStride, heightStride, 1);
|
|
uint32_t destRowStrideSize = formatSize(format, width, 1, 1);
|
|
uint32_t size = formatSize(format, width, height, depth);
|
|
|
|
for (uint32_t l = 0; l < region.texSubres.layerCount; l++) {
|
|
auto *buffer = allocator->allocate<uint8_t>(size, alignment);
|
|
uint32_t destOffset = 0;
|
|
uint32_t buffOffset = 0;
|
|
for (uint32_t d = 0; d < depth; d++) {
|
|
buffOffset = region.buffOffset + sliceStrideSize * d;
|
|
for (uint32_t h = 0; h < height; h += blockHeight) {
|
|
memcpy(buffer + destOffset, buffers[n] + buffOffset, destRowStrideSize);
|
|
destOffset += destRowStrideSize;
|
|
buffOffset += rowStrideSize;
|
|
}
|
|
}
|
|
actorBuffers[n++] = buffer;
|
|
}
|
|
actorRegions[i].buffOffset = 0;
|
|
actorRegions[i].buffStride = 0;
|
|
actorRegions[i].buffTexHeight = 0;
|
|
}
|
|
|
|
ENQUEUE_MESSAGE_6(
|
|
mq, DeviceCopyBuffersToTexture,
|
|
actor, actor,
|
|
buffers, actorBuffers,
|
|
dst, static_cast<TextureAgent *>(texture)->getActor(),
|
|
regions, actorRegions,
|
|
count, count,
|
|
allocator, allocator,
|
|
{
|
|
actor->copyBuffersToTexture(buffers, dst, regions, count);
|
|
delete allocator;
|
|
});
|
|
}
|
|
|
|
void DeviceAgent::copyBuffersToTexture(const uint8_t *const *buffers, Texture *dst, const BufferTextureCopy *regions, uint32_t count) {
|
|
doBufferTextureCopy(buffers, dst, regions, count, _mainMessageQueue, _actor);
|
|
}
|
|
|
|
void CommandBufferAgent::copyBuffersToTexture(const uint8_t *const *buffers, Texture *texture, const BufferTextureCopy *regions, uint32_t count) {
|
|
doBufferTextureCopy(buffers, texture, regions, count, _messageQueue, _actor);
|
|
}
|
|
|
|
void DeviceAgent::copyTextureToBuffers(Texture *srcTexture, uint8_t *const *buffers, const BufferTextureCopy *regions, uint32_t count) {
|
|
ENQUEUE_MESSAGE_5(
|
|
_mainMessageQueue,
|
|
DeviceCopyTextureToBuffers,
|
|
actor, getActor(),
|
|
src, static_cast<TextureAgent *>(srcTexture)->getActor(),
|
|
buffers, buffers,
|
|
regions, regions,
|
|
count, count,
|
|
{
|
|
actor->copyTextureToBuffers(src, buffers, regions, count);
|
|
});
|
|
|
|
_mainMessageQueue->kickAndWait();
|
|
}
|
|
|
|
void DeviceAgent::flushCommands(CommandBuffer *const *cmdBuffs, uint32_t count) {
|
|
if (!_multithreaded) return; // all command buffers are immediately executed
|
|
|
|
auto **agentCmdBuffs = _mainMessageQueue->allocate<CommandBufferAgent *>(count);
|
|
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
agentCmdBuffs[i] = static_cast<CommandBufferAgent *const>(cmdBuffs[i]);
|
|
MessageQueue::freeChunksInFreeQueue(agentCmdBuffs[i]->_messageQueue);
|
|
agentCmdBuffs[i]->_messageQueue->finishWriting();
|
|
}
|
|
|
|
ENQUEUE_MESSAGE_3(
|
|
_mainMessageQueue, DeviceFlushCommands,
|
|
count, count,
|
|
cmdBuffs, agentCmdBuffs,
|
|
multiThreaded, _actor->_multithreadedCommandRecording,
|
|
{
|
|
CommandBufferAgent::flushCommands(count, cmdBuffs, multiThreaded);
|
|
});
|
|
}
|
|
|
|
void DeviceAgent::getQueryPoolResults(QueryPool *queryPool) {
|
|
QueryPool *actorQueryPool = static_cast<QueryPoolAgent *>(queryPool)->getActor();
|
|
|
|
ENQUEUE_MESSAGE_2(
|
|
_mainMessageQueue, DeviceGetQueryPoolResults,
|
|
actor, getActor(),
|
|
queryPool, actorQueryPool,
|
|
{
|
|
actor->getQueryPoolResults(queryPool);
|
|
});
|
|
|
|
auto *actorQueryPoolAgent = static_cast<QueryPoolAgent *>(actorQueryPool);
|
|
auto *queryPoolAgent = static_cast<QueryPoolAgent *>(queryPool);
|
|
std::lock_guard<std::mutex> lock(actorQueryPoolAgent->_mutex);
|
|
queryPoolAgent->_results = actorQueryPoolAgent->_results;
|
|
}
|
|
|
|
void DeviceAgent::enableAutoBarrier(bool en) {
|
|
ENQUEUE_MESSAGE_2(
|
|
_mainMessageQueue, enableAutoBarrier,
|
|
actor, getActor(),
|
|
en, en,
|
|
{
|
|
actor->enableAutoBarrier(en);
|
|
});
|
|
}
|
|
|
|
void DeviceAgent::presentSignal() {
|
|
_frameBoundarySemaphore.signal();
|
|
}
|
|
|
|
void DeviceAgent::presentWait() {
|
|
MessageQueue::freeChunksInFreeQueue(_mainMessageQueue);
|
|
_mainMessageQueue->finishWriting();
|
|
_currentIndex = (_currentIndex + 1) % MAX_FRAME_INDEX;
|
|
_frameBoundarySemaphore.wait();
|
|
}
|
|
|
|
void DeviceAgent::frameSync() {
|
|
ENQUEUE_MESSAGE_1(
|
|
_mainMessageQueue, FrameSync,
|
|
actor, _actor,
|
|
{
|
|
actor->frameSync();
|
|
});
|
|
}
|
|
|
|
SampleCount DeviceAgent::getMaxSampleCount(Format format, TextureUsage usage, TextureFlags flags) const {
|
|
return _actor->getMaxSampleCount(format, usage, flags);
|
|
}
|
|
|
|
} // namespace gfx
|
|
} // namespace cc
|
|
|