Skip to content
17 changes: 17 additions & 0 deletions include/API/Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ class Buffer {
Buffer() = default;
};

class Fence {
public:
virtual ~Fence() = default;

Fence(const Fence &) = delete;
Fence &operator=(const Fence &) = delete;

virtual uint64_t getFenceValue() = 0;
virtual llvm::Error waitForCompletion(uint64_t SignalValue) = 0;

protected:
Fence() = default;
};

class Queue {
public:
virtual ~Queue() = 0;
Expand All @@ -77,6 +91,9 @@ class Device {

virtual Queue &getGraphicsQueue() = 0;

virtual llvm::Expected<std::unique_ptr<Fence>>
createFence(llvm::StringRef Name) = 0;

virtual llvm::Expected<std::shared_ptr<Buffer>>
createBuffer(std::string Name, BufferCreateDesc &Desc,
size_t SizeInBytes) = 0;
Expand Down
139 changes: 90 additions & 49 deletions lib/API/DX/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,82 @@ class DXBuffer : public offloadtest::Buffer {
: Buffer(Buffer), Name(Name), Desc(Desc), SizeInBytes(SizeInBytes) {}
};

class DXFence : public offloadtest::Fence {
public:
#ifdef _WIN32
DXFence(ComPtr<ID3D12Fence> Fence, HANDLE Event, llvm::StringRef Name)
#else // WSL
DXFence(ComPtr<ID3D12Fence> Fence, int Event, llvm::StringRef Name)
#endif
: Name(Name), Fence(Fence), Event(Event) {
}

std::string Name;
ComPtr<ID3D12Fence> Fence;
#ifdef _WIN32
HANDLE Event;
#else // WSL
int Event;
#endif

static llvm::Expected<std::unique_ptr<DXFence>> create(ID3D12Device *Device,
llvm::StringRef Name) {
ComPtr<ID3D12Fence> Fence;
if (auto Err = HR::toError(
Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&Fence)),
"Failed to create Fence."))
return Err;

#ifdef _WIN32
HANDLE Event = CreateEventA(nullptr, false, false, nullptr);
if (!Event)
#else // WSL
int Event = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (Event == -1)
#endif
return llvm::createStringError(std::errc::device_or_resource_busy,
"Failed to create event.");

return std::make_unique<DXFence>(Fence, Event, Name);
}

~DXFence() {
#ifdef _WIN32
CloseHandle(Event);
#else // WSL
close(Event);
#endif
}

uint64_t getFenceValue() override { return Fence->GetCompletedValue(); }

llvm::Error waitForCompletion(uint64_t SignalValue) override {
if (Fence->GetCompletedValue() >= SignalValue)
return llvm::Error::success();

#ifdef _WIN32
if (auto Err = HR::toError(Fence->SetEventOnCompletion(SignalValue, Event),
"Failed to register end event."))
return Err;
WaitForSingleObject(Event, INFINITE);
#else // WSL
if (auto Err =
HR::toError(Fence->SetEventOnCompletion(
SignalValue, reinterpret_cast<HANDLE>(Event)),
"Failed to register end event."))
return Err;
pollfd PollEvent;
PollEvent.fd = Event;
PollEvent.events = POLLIN;
PollEvent.revents = 0;
if (poll(&PollEvent, 1, -1) == -1)
return llvm::createStringError(
std::error_code(errno, std::system_category()), strerror(errno));
#endif
return llvm::Error::success();
}
};

class DXQueue : public offloadtest::Queue {
public:
ComPtr<ID3D12CommandQueue> Queue;
Expand Down Expand Up @@ -346,12 +422,7 @@ class DXDevice : public offloadtest::Device {
ComPtr<ID3D12PipelineState> PSO;
ComPtr<ID3D12CommandAllocator> Allocator;
ComPtr<ID3D12GraphicsCommandList> CmdList;
ComPtr<ID3D12Fence> Fence;
#ifdef _WIN32
HANDLE Event;
#else // WSL
int Event;
#endif
std::unique_ptr<offloadtest::Fence> Fence;

// Resources for graphics pipelines.
ComPtr<ID3D12Resource> RT;
Expand All @@ -378,6 +449,11 @@ class DXDevice : public offloadtest::Device {

Queue &getGraphicsQueue() override { return GraphicsQueue; }

llvm::Expected<std::unique_ptr<offloadtest::Fence>>
createFence(llvm::StringRef Name) override {
return DXFence::create(Device.Get(), Name);
}

llvm::Expected<std::shared_ptr<offloadtest::Buffer>>
createBuffer(std::string Name, BufferCreateDesc &Desc,
size_t SizeInBytes) override {
Expand Down Expand Up @@ -1136,56 +1212,20 @@ class DXDevice : public offloadtest::Device {
IS.CmdList->ResourceBarrier(1, &Barrier);
}

llvm::Error createEvent(InvocationState &IS) {
if (auto Err = HR::toError(Device->CreateFence(0, D3D12_FENCE_FLAG_NONE,
IID_PPV_ARGS(&IS.Fence)),
"Failed to create fence."))
return Err;
#ifdef _WIN32
IS.Event = CreateEventA(nullptr, false, false, nullptr);
if (!IS.Event)
#else // WSL
IS.Event = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (IS.Event == -1)
#endif
return llvm::createStringError(std::errc::device_or_resource_busy,
"Failed to create event.");
return llvm::Error::success();
}

llvm::Error waitForSignal(InvocationState &IS) {
// This is a hack but it works since this is all single threaded code.
static uint64_t FenceCounter = 0;
const uint64_t CurrentCounter = FenceCounter + 1;
auto *F = static_cast<DXFence *>(IS.Fence.get());

if (auto Err = HR::toError(
GraphicsQueue.Queue->Signal(IS.Fence.Get(), CurrentCounter),
GraphicsQueue.Queue->Signal(F->Fence.Get(), CurrentCounter),
"Failed to add signal."))
return Err;

if (IS.Fence->GetCompletedValue() < CurrentCounter) {
#ifdef _WIN32
HANDLE Event = IS.Event;
#else // WSL
HANDLE Event = reinterpret_cast<HANDLE>(IS.Event);
#endif
if (auto Err =
HR::toError(IS.Fence->SetEventOnCompletion(CurrentCounter, Event),
"Failed to register end event."))
return Err;
if (auto Err = IS.Fence->waitForCompletion(CurrentCounter))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why IS.Fence is being used here, instead of F?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the goal is to use the new abstract interface. The only reason the fence is being downcast in the first place is because we don't have an abstract function for submitting on the queue yet.

return Err;

#ifdef _WIN32
WaitForSingleObject(IS.Event, INFINITE);
#else // WSL
pollfd PollEvent;
PollEvent.fd = IS.Event;
PollEvent.events = POLLIN;
PollEvent.revents = 0;
if (poll(&PollEvent, 1, -1) == -1)
return llvm::createStringError(
std::error_code(errno, std::system_category()), strerror(errno));
#endif
}
FenceCounter = CurrentCounter;
return llvm::Error::success();
}
Expand Down Expand Up @@ -1690,9 +1730,10 @@ class DXDevice : public offloadtest::Device {
return Err;
llvm::outs() << "Command structures created.\n";

if (auto Err = createEvent(State))
return Err;
llvm::outs() << "Event prepared.\n";
auto FenceOrErr = createFence("Fence");
if (!FenceOrErr)
return FenceOrErr.takeError();
State.Fence = std::move(*FenceOrErr);

if (auto Err = createBuffers(P, State))
return Err;
Expand Down
53 changes: 52 additions & 1 deletion lib/API/MTL/MTLDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,37 @@ class MTLQueue : public offloadtest::Queue {
}
};

class MTLFence : public offloadtest::Fence {
public:
MTLFence(MTL::SharedEvent *Event, llvm::StringRef Name)
: Name(Name), Event(Event) {}
std::string Name;
MTL::SharedEvent *Event;

static llvm::Expected<std::unique_ptr<MTLFence>>
create(MTL::Device *Device, llvm::StringRef Name) {
MTL::SharedEvent *Event = Device->newSharedEvent();
if (!Event)
return llvm::createStringError(std::errc::device_or_resource_busy,
"Failed to create shared event.");
return std::make_unique<MTLFence>(Event, Name);
}

~MTLFence() {
if (Event)
Event->release();
}

uint64_t getFenceValue() override { return Event->signaledValue(); }

llvm::Error waitForCompletion(uint64_t SignalValue) override {
if (!Event->waitUntilSignaledValue(SignalValue, UINT64_MAX))
return llvm::createStringError(std::errc::timed_out,
"Timed out waiting on shared event.");
return llvm::Error::success();
}
};

class MTLBuffer : public offloadtest::Buffer {
public:
MTL::Buffer *Buf;
Expand Down Expand Up @@ -130,6 +161,7 @@ class MTLDevice : public offloadtest::Device {
llvm::SmallVector<MTL::Buffer *> Buffers;
MTL::Texture *FrameBufferTexture = nullptr;
MTL::CommandBuffer *CmdBuffer = nullptr;
std::unique_ptr<offloadtest::Fence> Fence;
};

llvm::Error setupVertexShader(InvocationState &IS, const Pipeline &P,
Expand Down Expand Up @@ -488,14 +520,23 @@ class MTLDevice : public offloadtest::Device {
}

llvm::Error executeCommands(InvocationState &IS) {
// This is a hack but it works since this is all single threaded code.
static uint64_t FenceCounter = 0;
const uint64_t CurrentCounter = FenceCounter + 1;
auto *F = static_cast<MTLFence *>(IS.Fence.get());

IS.CmdBuffer->encodeSignalEvent(F->Event, CurrentCounter);
IS.CmdBuffer->commit();
IS.CmdBuffer->waitUntilCompleted();

if (auto Err = IS.Fence->waitForCompletion(CurrentCounter))
return Err;

// Check and surface any errors that occurred during execution.
NS::Error *CBErr = IS.CmdBuffer->error();
if (CBErr)
return toError(CBErr);

FenceCounter = CurrentCounter;
return llvm::Error::success();
}

Expand Down Expand Up @@ -565,6 +606,11 @@ class MTLDevice : public offloadtest::Device {

Queue &getGraphicsQueue() override { return GraphicsQueue; }

llvm::Expected<std::unique_ptr<offloadtest::Fence>>
createFence(llvm::StringRef Name) override {
return MTLFence::create(Device, Name);
}

llvm::Expected<std::shared_ptr<offloadtest::Buffer>>
createBuffer(std::string Name, BufferCreateDesc &Desc,
size_t SizeInBytes) override {
Expand All @@ -589,6 +635,11 @@ class MTLDevice : public offloadtest::Device {
llvm::Error executeProgram(Pipeline &P) override {
InvocationState IS;

auto FenceOrErr = createFence("Fence");
if (!FenceOrErr)
return FenceOrErr.takeError();
IS.Fence = std::move(*FenceOrErr);

if (auto Err = createBuffers(P, IS))
return Err;

Expand Down
Loading
Loading