Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Engine/gapi/abstractgraphicsapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,8 @@ std::shared_ptr<AbstractGraphicsApi::Fence> AbstractGraphicsApi::submit(Device*
NonUniqResId AbstractGraphicsApi::Swapchain::syncId() const {
return NonUniqResId(0x1);
}

AbstractGraphicsApi::Swapchain* AbstractGraphicsApi::createSwapchain(SystemApi::Window* w, Device* d,
const Swapchain::Options&) {
return createSwapchain(w,d);
}
11 changes: 11 additions & 0 deletions Engine/gapi/abstractgraphicsapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,16 @@ namespace Tempest {
virtual void setPayload(std::vector<Detail::DSharedPtr<const AbstractGraphicsApi::Shared*>>&&) {}
};
struct Swapchain:NoCopy {
enum class RenderMode:uint8_t {
Copy,
Direct,
};

struct Options {
uint32_t bufferCount = 0;
RenderMode renderMode = RenderMode::Copy;
};

virtual ~Swapchain()=default;
virtual void reset()=0;
virtual uint32_t currentBackBufferIndex()=0;
Expand Down Expand Up @@ -636,6 +646,7 @@ namespace Tempest {
virtual Device* createDevice(std::string_view gpuName) = 0;

virtual Swapchain* createSwapchain(SystemApi::Window* w,AbstractGraphicsApi::Device *d) = 0;
virtual Swapchain* createSwapchain(SystemApi::Window* w,Device* d,const Swapchain::Options& options);

virtual PPipeline createPipeline(Device* d, const RenderState &st, Topology tp, const Shader* const* sh, size_t cnt)=0;

Expand Down
8 changes: 7 additions & 1 deletion Engine/gapi/metal/mtcommandbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "mtswapchain.h"
#include "mtaccelerationstructure.h"

#include <algorithm>

using namespace Tempest;
using namespace Tempest::Detail;

Expand Down Expand Up @@ -65,6 +67,7 @@ void MtCommandBuffer::end() {
}

void MtCommandBuffer::reset() {
swapchainFrames.clear();
auto pool = NsPtr<NS::AutoreleasePool>::init();
auto desc = NsPtr<MTL::CommandBufferDescriptor>::init();
desc->setRetainedReferences(false);
Expand Down Expand Up @@ -94,7 +97,10 @@ void MtCommandBuffer::beginRendering(const FrameBufferDesc& fbo, size_t fboSize,
auto clr = desc->colorAttachments()->object(i);
if(fbo.sw[i]!=nullptr) {
auto& s = *reinterpret_cast<MtSwapchain*>(fbo.sw[i]);
clr->setTexture(s.img[fbo.imgId[i]].tex.get());
auto frame = s.acquireFrame(fbo.imgId[i]);
clr->setTexture(frame==nullptr ? s.img[fbo.imgId[i]].tex.get() : frame->texture.get());
if(frame!=nullptr && std::find(swapchainFrames.begin(),swapchainFrames.end(),frame)==swapchainFrames.end())
swapchainFrames.push_back(std::move(frame));
curFbo.colorFormat[curFbo.numColors] = s.format();
} else {
auto& t = *reinterpret_cast<MtTexture*>(fbo.att[i]);
Expand Down
2 changes: 2 additions & 0 deletions Engine/gapi/metal/mtcommandbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class MtPipeline;
class MtCompPipeline;
class MtDescriptorArray;
class MtTopAccelerationStructure;
struct MtSwapchainFrame;

class MtCommandBuffer : public AbstractGraphicsApi::CommandBuffer {
public:
Expand Down Expand Up @@ -113,6 +114,7 @@ class MtCommandBuffer : public AbstractGraphicsApi::CommandBuffer {
NsPtr<MTL::BlitCommandEncoder> encBlit;

std::vector<const void*> usedResources;
std::vector<std::shared_ptr<MtSwapchainFrame>> swapchainFrames;

MtFboLayout curFbo;
Push pushData;
Expand Down
19 changes: 16 additions & 3 deletions Engine/gapi/metal/mtswapchain.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#pragma once

#include <Tempest/AbstractGraphicsApi>
#include "utility/spinlock.h"
#include "nsptr.h"

#include <Metal/Metal.hpp>
#include <mutex>

namespace CA
{
Expand All @@ -16,9 +16,19 @@ namespace Detail {

class MtDevice;

struct MtSwapchainFrame {
MtSwapchainFrame(MTL::Texture* texture, CA::MetalDrawable* drawable);
~MtSwapchainFrame();

NsPtr<MTL::Texture> texture;
NsPtr<CA::MetalDrawable> drawable;
};

class MtSwapchain : public AbstractGraphicsApi::Swapchain {
public:
MtSwapchain(MtDevice& dev, SystemApi::Window* w);
using Frame = std::shared_ptr<MtSwapchainFrame>;

MtSwapchain(MtDevice& dev, SystemApi::Window* w, const Options& options);
~MtSwapchain();

void reset() override;
Expand All @@ -30,6 +40,7 @@ class MtSwapchain : public AbstractGraphicsApi::Swapchain {
NonUniqResId syncId() const override { return NonUniqResId::I_None; }

MTL::PixelFormat format() const;
Frame acquireFrame(uint32_t image);

struct Image {
NsPtr<MTL::Texture> tex;
Expand All @@ -40,12 +51,14 @@ class MtSwapchain : public AbstractGraphicsApi::Swapchain {
struct Impl;
std::unique_ptr<Impl> pimpl;

SpinLock sync;
std::mutex sync;
MtDevice& dev;
Tempest::Size sz;

uint32_t imgCount = 0;
uint32_t currentImg = 0;
bool direct = false;
Frame activeFrame;

NsPtr<MTL::Texture> mkTexture();
void nextDrawable();
Expand Down
101 changes: 70 additions & 31 deletions Engine/gapi/metal/mtswapchain.mm
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ - (CALayer *)makeBackingLayer {
}
};

MtSwapchainFrame::MtSwapchainFrame(MTL::Texture* tex, CA::MetalDrawable* dr)
:texture(tex), drawable(dr) {
if(tex!=nullptr)
tex->retain();
if(dr!=nullptr)
dr->retain();
}

MtSwapchainFrame::~MtSwapchainFrame() {
}

static float backingScaleFactor(SysWindow* w) {
#if defined(__OSX__)
return [w screen].backingScaleFactor;
Expand Down Expand Up @@ -92,8 +103,8 @@ static CGRect windowRect(UIWindow* wnd) {
#endif

// note : MoltenVK supports NSView, UIView, CAMetalLayer, so we should align to it
MtSwapchain::MtSwapchain(MtDevice& dev, SystemApi::Window *w)
:dev(dev), pimpl(new Impl()) {
MtSwapchain::MtSwapchain(MtDevice& dev, SystemApi::Window *w, const Options& options)
:pimpl(new Impl()), dev(dev), direct(options.renderMode==RenderMode::Direct) {
NSObject* obj = reinterpret_cast<NSObject*>(w);
if([obj isKindOfClass : [SysWindow class]])
pimpl->wnd = reinterpret_cast<SysWindow*>(w);
Expand All @@ -117,23 +128,28 @@ static CGRect windowRect(UIWindow* wnd) {
[lay setContentsScale:dpi];
#if defined(__IOS__)
// Swapchain takes too much memory on 2GB iPhone
lay.maximumDrawableCount = 2;
lay.maximumDrawableCount = options.bufferCount==0 ? 2 : options.bufferCount;
#elif defined(__OSX__)
if(options.bufferCount!=0)
lay.maximumDrawableCount = options.bufferCount;
#endif
lay.pixelFormat = MTLPixelFormatBGRA8Unorm;
lay.allowsNextDrawableTimeout = NO;
lay.allowsNextDrawableTimeout = direct ? YES : NO;
lay.framebufferOnly = NO;

reset();
}

MtSwapchain::~MtSwapchain() {
dev.waitIdle();
if(pimpl->view!=nil)
[pimpl->view release];
}

void MtSwapchain::reset() {
std::lock_guard<std::mutex> guard(sync);
dev.waitIdle(); // pending commands
std::lock_guard<SpinLock> guard(sync);
activeFrame.reset();

// https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc
CAMetalLayer* lay = pimpl->metalLayer();
Expand All @@ -146,8 +162,10 @@ static CGRect windowRect(UIWindow* wnd) {
img.resize(imgCount);
for(size_t i=0; i<imgCount; ++i)
img[i].tex = nullptr;
for(size_t i=0; i<imgCount; ++i)
img[i].tex = mkTexture();
if(!direct) {
for(size_t i=0; i<imgCount; ++i)
img[i].tex = mkTexture();
}

currentImg = 0;
}
Expand All @@ -156,54 +174,75 @@ static CGRect windowRect(UIWindow* wnd) {
return currentImg;
}

MtSwapchain::Frame MtSwapchain::acquireFrame(uint32_t image) {
// Keep the existing Copy path free of per-frame ownership allocations.
if(!direct)
return nullptr;
std::lock_guard<std::mutex> guard(sync);
if(image!=currentImg)
throw SwapchainSuboptimal();
if(activeFrame!=nullptr)
return activeFrame;

auto pool = NsPtr<NS::AutoreleasePool>::init();
auto* lay = reinterpret_cast<CA::MetalLayer*>(pimpl->metalLayer());
auto* dr = lay->nextDrawable();
if(dr!=nullptr && dr->texture()->width()==size_t(sz.w) && dr->texture()->height()==size_t(sz.h)) {
activeFrame = std::make_shared<MtSwapchainFrame>(dr->texture(),dr);
} else {
// Allocate the private Copy fallback only when drawable acquisition fails.
if(img[image].tex==nullptr)
img[image].tex = mkTexture();
activeFrame = std::make_shared<MtSwapchainFrame>(img[image].tex.get(),nullptr);
}
return activeFrame;
}

void MtSwapchain::present() {
std::lock_guard<std::mutex> guard(sync);
auto pool = NsPtr<NS::AutoreleasePool>::init();
auto frame = activeFrame;
if(direct && frame==nullptr)
throw SwapchainSuboptimal();

CA::MetalLayer* lay = reinterpret_cast<CA::MetalLayer*>(pimpl->metalLayer());
uint32_t i = currentImg;
auto drawable = lay->nextDrawable();
auto drawable = frame==nullptr ? nullptr : frame->drawable.get();
if(drawable==nullptr)
drawable = lay->nextDrawable();
if(drawable==nullptr)
throw SwapchainSuboptimal();

std::lock_guard<SpinLock> guard(sync);
auto src = frame==nullptr ? img[i].tex.get() : frame->texture.get();
auto dr = drawable->texture();
if(dr->width()!=img[i].tex->width() || dr->height()!=img[i].tex->height()) {
if(dr->width()!=src->width() || dr->height()!=src->height()) {
throw SwapchainSuboptimal();
}

auto desc = NsPtr<MTL::CommandBufferDescriptor>::init();
//desc->setRetainedReferences(true);
desc->setRetainedReferences(true);
desc->setErrorOptions(MTL::CommandBufferErrorOptionEncoderExecutionStatus);

auto cmd = dev.queue->commandBuffer(desc.get());
auto enc = cmd->blitCommandEncoder();

enc->copyFromTexture(img[i].tex.get(), 0, 0,
dr, 0, 0,
1, 1);
enc->endEncoding();
if(src!=dr) {
auto enc = cmd->blitCommandEncoder();
enc->copyFromTexture(src, 0, 0,
dr, 0, 0,
1, 1);
enc->endEncoding();
}
cmd->presentDrawable(drawable);

auto* device = &dev;
dev.onSubmit();
cmd->addCompletedHandler(^(MTL::CommandBuffer* c){
MTL::CommandBufferStatus s = c->status();
if(s==MTL::CommandBufferStatusNotEnqueued ||
s==MTL::CommandBufferStatusEnqueued ||
s==MTL::CommandBufferStatusCommitted ||
s==MTL::CommandBufferStatusScheduled)
return;

if(s!=MTL::CommandBufferStatusCompleted) {
if(c->status()!=MTL::CommandBufferStatusCompleted)
Log::e("swapchain fatal error");
dev.onFinish();
dev.waitIdle();
return;
}

dev.onFinish();
device->onFinish();
});
cmd->commit();

activeFrame.reset();
nextDrawable();
}

Expand Down
20 changes: 19 additions & 1 deletion Engine/gapi/metalapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

#include <Metal/Metal.hpp>

#include <stdexcept>

using namespace Tempest;
using namespace Tempest::Detail;

Expand Down Expand Up @@ -68,8 +70,17 @@ AbstractGraphicsApi::Device* MetalApi::createDevice(std::string_view gpuName) {

AbstractGraphicsApi::Swapchain *MetalApi::createSwapchain(SystemApi::Window *w,
AbstractGraphicsApi::Device* d) {
return createSwapchain(w,d,Swapchain::Options{});
}

AbstractGraphicsApi::Swapchain* MetalApi::createSwapchain(SystemApi::Window* w, Device* d,
const Swapchain::Options& options) {
if(options.bufferCount!=0 && options.bufferCount!=2 && options.bufferCount!=3)
throw std::invalid_argument("Metal swapchain buffer count must be 0, 2, or 3");
if(options.renderMode!=Swapchain::RenderMode::Copy && options.renderMode!=Swapchain::RenderMode::Direct)
throw std::invalid_argument("Unknown Metal swapchain render mode");
auto& dev = *reinterpret_cast<MtDevice*>(d);
return new MtSwapchain(dev,w);
return new MtSwapchain(dev,w,options);
}

AbstractGraphicsApi::PPipeline MetalApi::createPipeline(AbstractGraphicsApi::Device *d,
Expand Down Expand Up @@ -203,8 +214,15 @@ std::shared_ptr<AbstractGraphicsApi::Fence> MetalApi::submit(Device* d, CommandB
throw DeviceLostException();

MTL::CommandBuffer& cmd = *cx.impl;
std::shared_ptr<std::vector<MtSwapchain::Frame>> frames;
if(!cx.swapchainFrames.empty()) {
frames = std::make_shared<std::vector<MtSwapchain::Frame>>(std::move(cx.swapchainFrames));
}
dx->onSubmit();
cmd.addCompletedHandler(^(MTL::CommandBuffer* c){
// A completed command buffer may itself remain alive until the next frame.
if(frames!=nullptr)
frames->clear();
const MTL::CommandBufferStatus s = c->status();
dx->signalFence(*pfence, s, MTL::CommandBufferError(c->error()->code()), c->error());
if(s==MTL::CommandBufferStatusCompleted || s==MTL::CommandBufferStatusError)
Expand Down
1 change: 1 addition & 0 deletions Engine/gapi/metalapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class MetalApi : public AbstractGraphicsApi {
protected:
Device* createDevice(std::string_view gpuName) override;
Swapchain* createSwapchain(SystemApi::Window* w, Device *d) override;
Swapchain* createSwapchain(SystemApi::Window* w, Device* d, const Swapchain::Options& options) override;

PPipeline createPipeline(Device* d, const RenderState &st, Topology tp,
const Shader*const* sh, size_t cnt) override;
Expand Down
4 changes: 4 additions & 0 deletions Engine/graphics/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ Swapchain Device::swapchain(SystemApi::Window* w) const {
return Swapchain(api.createSwapchain(w,impl.dev));
}

Swapchain Device::swapchain(SystemApi::Window* w, const Swapchain::Options& options) const {
return Swapchain(api.createSwapchain(w,impl.dev,options));
}

const Device::Props& Device::properties() const {
return devProps;
}
Expand Down
1 change: 1 addition & 0 deletions Engine/graphics/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class Device {
void present(Swapchain& sw);

Swapchain swapchain(SystemApi::Window* w) const;
Swapchain swapchain(SystemApi::Window* w, const Swapchain::Options& options) const;

Shader shader(RFile& file);
Shader shader(const char* filename);
Expand Down
4 changes: 4 additions & 0 deletions Engine/graphics/swapchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Swapchain::Swapchain(Device& dev, SystemApi::Window* w) {
*this = dev.swapchain(w);
}

Swapchain::Swapchain(Device& dev, SystemApi::Window* w, const Options& options) {
*this = dev.swapchain(w,options);
}

Swapchain::~Swapchain() {
delete impl.handler;
}
Expand Down
Loading
Loading