mirror of
https://github.com/AxioDL/metaforce.git
synced 2025-12-18 03:25:24 +00:00
aurora: Working movie player (again)
This commit is contained in:
@@ -1,10 +1,20 @@
|
||||
#include "common.hpp"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "../gpu.hpp"
|
||||
#include "movie_player/shader.hpp"
|
||||
|
||||
#include <logvisor/logvisor.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace aurora::gfx {
|
||||
static logvisor::Module Log("aurora::gfx");
|
||||
|
||||
using gpu::g_device;
|
||||
using gpu::g_queue;
|
||||
|
||||
struct ShaderState {
|
||||
movie_player::State moviePlayer;
|
||||
};
|
||||
struct ShaderDrawCommand {
|
||||
ShaderType type;
|
||||
union {
|
||||
@@ -45,9 +55,46 @@ zeus::CMatrix4f g_mvInv;
|
||||
zeus::CMatrix4f g_proj;
|
||||
metaforce::CFogState g_fogState;
|
||||
|
||||
std::vector<Command> g_commands;
|
||||
static std::mutex g_pipelineMutex;
|
||||
static std::unordered_map<uint64_t, wgpu::RenderPipeline> g_pipelines;
|
||||
static std::vector<PipelineCreateCommand> g_queuedPipelines;
|
||||
static std::unordered_map<BindGroupRef, wgpu::BindGroup> g_cachedBindGroups;
|
||||
|
||||
bool get_dxt_compression_supported() noexcept { return g_Device.HasFeature(wgpu::FeatureName::TextureCompressionBC); }
|
||||
static ByteBuffer g_verts;
|
||||
static ByteBuffer g_uniforms;
|
||||
static ByteBuffer g_indices;
|
||||
wgpu::Buffer g_vertexBuffer;
|
||||
wgpu::Buffer g_uniformBuffer;
|
||||
wgpu::Buffer g_indexBuffer;
|
||||
|
||||
static ShaderState g_state;
|
||||
static PipelineRef g_currentPipeline;
|
||||
|
||||
static std::vector<Command> g_commands;
|
||||
|
||||
using NewPipelineCallback = std::function<wgpu::RenderPipeline()>;
|
||||
static PipelineRef find_pipeline(PipelineCreateCommand command, NewPipelineCallback cb) {
|
||||
const auto hash = xxh3_hash(command);
|
||||
bool found;
|
||||
{
|
||||
std::lock_guard guard{g_pipelineMutex};
|
||||
const auto ref = g_pipelines.find(hash);
|
||||
found = ref != g_pipelines.end();
|
||||
}
|
||||
if (!found) {
|
||||
// TODO another thread
|
||||
wgpu::RenderPipeline pipeline = cb();
|
||||
{
|
||||
std::lock_guard guard{g_pipelineMutex};
|
||||
g_pipelines[hash] = std::move(pipeline);
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void push_draw_command(ShaderDrawCommand data) { g_commands.push_back({CommandType::Draw, {.draw = data}}); }
|
||||
|
||||
bool get_dxt_compression_supported() noexcept { return g_device.HasFeature(wgpu::FeatureName::TextureCompressionBC); }
|
||||
|
||||
void update_model_view(const zeus::CMatrix4f& mv, const zeus::CMatrix4f& mv_inv) noexcept {
|
||||
g_mv = mv;
|
||||
@@ -98,8 +145,134 @@ void queue_colored_quad(CameraFilterType filter_type, ZTest z_comparison, bool z
|
||||
const zeus::CRectangle& rect, float z) noexcept {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void queue_movie_player(const TextureHandle& tex_y, const TextureHandle& tex_u, const TextureHandle& tex_v,
|
||||
const zeus::CColor& color, float h_pad, float v_pad) noexcept {
|
||||
// TODO
|
||||
auto data = movie_player::make_draw_data(g_state.moviePlayer, tex_y, tex_u, tex_v, color, h_pad, v_pad);
|
||||
push_draw_command({.type = ShaderType::MoviePlayer, .moviePlayer = data});
|
||||
}
|
||||
template <>
|
||||
PipelineRef pipeline_ref(movie_player::PipelineConfig config) {
|
||||
return find_pipeline({.type = ShaderType::MoviePlayer, .moviePlayer = config},
|
||||
[=]() { return create_pipeline(g_state.moviePlayer, config); });
|
||||
}
|
||||
|
||||
void construct_state() {
|
||||
{
|
||||
const auto uniformDescriptor = wgpu::BufferDescriptor{
|
||||
.label = "Shared Uniform Buffer",
|
||||
.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst,
|
||||
.size = 134217728, // 128mb
|
||||
};
|
||||
g_uniformBuffer = g_device.CreateBuffer(&uniformDescriptor);
|
||||
}
|
||||
{
|
||||
const auto vertexDescriptor = wgpu::BufferDescriptor{
|
||||
.label = "Shared Vertex Buffer",
|
||||
.usage = wgpu::BufferUsage::Vertex | wgpu::BufferUsage::CopyDst,
|
||||
.size = 16777216, // 16mb
|
||||
};
|
||||
g_vertexBuffer = g_device.CreateBuffer(&vertexDescriptor);
|
||||
}
|
||||
{
|
||||
const auto vertexDescriptor = wgpu::BufferDescriptor{
|
||||
.label = "Shared Index Buffer",
|
||||
.usage = wgpu::BufferUsage::Vertex | wgpu::BufferUsage::CopyDst,
|
||||
.size = 4194304, // 4mb
|
||||
};
|
||||
g_indexBuffer = g_device.CreateBuffer(&vertexDescriptor);
|
||||
}
|
||||
|
||||
g_state.moviePlayer = movie_player::construct_state();
|
||||
}
|
||||
|
||||
void render(const wgpu::RenderPassEncoder& pass) {
|
||||
{
|
||||
g_queue.WriteBuffer(g_vertexBuffer, 0, g_verts.data(), g_verts.size());
|
||||
g_queue.WriteBuffer(g_uniformBuffer, 0, g_uniforms.data(), g_uniforms.size());
|
||||
g_queue.WriteBuffer(g_indexBuffer, 0, g_indices.data(), g_indices.size());
|
||||
g_verts.clear();
|
||||
g_uniforms.clear();
|
||||
g_indices.clear();
|
||||
}
|
||||
|
||||
g_currentPipeline = UINT64_MAX;
|
||||
|
||||
for (const auto& cmd : g_commands) {
|
||||
switch (cmd.type) {
|
||||
case CommandType::SetViewport: {
|
||||
const auto& vp = cmd.data.setViewport;
|
||||
pass.SetViewport(vp.rect.position.x(), vp.rect.position.y(), vp.rect.size.x(), vp.rect.size.y(), vp.znear,
|
||||
vp.zfar);
|
||||
} break;
|
||||
case CommandType::SetScissor: {
|
||||
const auto& sc = cmd.data.setScissor;
|
||||
pass.SetScissorRect(sc.x, sc.y, sc.w, sc.h);
|
||||
} break;
|
||||
case CommandType::Draw: {
|
||||
const auto& draw = cmd.data.draw;
|
||||
switch (draw.type) {
|
||||
case ShaderType::Aabb:
|
||||
// TODO
|
||||
break;
|
||||
case ShaderType::TexturedQuad:
|
||||
// TODO
|
||||
break;
|
||||
case ShaderType::MoviePlayer:
|
||||
movie_player::render(g_state.moviePlayer, draw.moviePlayer, pass);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
g_commands.clear();
|
||||
}
|
||||
|
||||
bool bind_pipeline(PipelineRef ref, const wgpu::RenderPassEncoder& pass) {
|
||||
if (ref == g_currentPipeline) {
|
||||
return true;
|
||||
}
|
||||
std::lock_guard guard{g_pipelineMutex};
|
||||
if (!g_pipelines.contains(ref)) {
|
||||
return false;
|
||||
}
|
||||
pass.SetPipeline(g_pipelines[ref]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline Range push(ByteBuffer& target, const uint8_t* data, size_t length, size_t alignment) {
|
||||
size_t padding = 0;
|
||||
if (alignment != 0) {
|
||||
padding = alignment - length % alignment;
|
||||
}
|
||||
auto begin = target.size();
|
||||
target.append(data, length);
|
||||
if (padding > 0) {
|
||||
target.append_zeroes(padding);
|
||||
}
|
||||
return {begin, begin + length};
|
||||
}
|
||||
Range push_verts(const uint8_t* data, size_t length) { return push(g_verts, data, length, 0 /* TODO? */); }
|
||||
Range push_indices(const uint8_t* data, size_t length) { return push(g_indices, data, length, 0 /* TODO? */); }
|
||||
Range push_uniform(const uint8_t* data, size_t length) {
|
||||
wgpu::SupportedLimits limits;
|
||||
g_device.GetLimits(&limits);
|
||||
return push(g_uniforms, data, length, limits.limits.minUniformBufferOffsetAlignment);
|
||||
}
|
||||
|
||||
BindGroupRef bind_group_ref(const wgpu::BindGroupDescriptor& descriptor) {
|
||||
const auto id =
|
||||
xxh3_hash(descriptor.entries, descriptor.entryCount * sizeof(wgpu::BindGroupEntry), xxh3_hash(descriptor));
|
||||
if (!g_cachedBindGroups.contains(id)) {
|
||||
g_cachedBindGroups[id] = g_device.CreateBindGroup(&descriptor);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
const wgpu::BindGroup& find_bind_group(BindGroupRef id) {
|
||||
if (!g_cachedBindGroups.contains(id)) {
|
||||
Log.report(logvisor::Fatal, FMT_STRING("get_bind_group: failed to locate {}"), id);
|
||||
}
|
||||
return g_cachedBindGroups[id];
|
||||
}
|
||||
} // namespace aurora::gfx
|
||||
|
||||
@@ -9,24 +9,103 @@
|
||||
#include <xxh_x86dispatch.h>
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
XXH64_hash_t xxh3_hash(const T& input, XXH64_hash_t seed = 0) {
|
||||
return XXH3_64bits_withSeed(&input, sizeof(T), seed);
|
||||
}
|
||||
#ifndef ALIGN
|
||||
#define ALIGN(x, a) (((x) + ((a)-1)) & ~((a)-1))
|
||||
#endif
|
||||
|
||||
namespace aurora {
|
||||
extern wgpu::Device g_Device;
|
||||
extern wgpu::Queue g_Queue;
|
||||
static inline XXH64_hash_t xxh3_hash(const void* input, size_t len, XXH64_hash_t seed = 0) {
|
||||
return XXH3_64bits_withSeed(input, len, seed);
|
||||
}
|
||||
template <typename T>
|
||||
static inline XXH64_hash_t xxh3_hash(const T& input, XXH64_hash_t seed = 0) {
|
||||
return xxh3_hash(&input, sizeof(T), seed);
|
||||
}
|
||||
|
||||
class ByteBuffer {
|
||||
public:
|
||||
ByteBuffer() = default;
|
||||
explicit ByteBuffer(size_t capacity) : m_data(static_cast<uint8_t*>(calloc(1, capacity))), m_capacity(capacity) {}
|
||||
|
||||
~ByteBuffer() {
|
||||
if (m_data != nullptr) {
|
||||
free(m_data);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* data() { return m_data; }
|
||||
const uint8_t* data() const { return m_data; }
|
||||
size_t size() const { return m_length; }
|
||||
|
||||
void append(const void* data, size_t size) {
|
||||
resize(m_length + size);
|
||||
memcpy(m_data + m_length, data, size);
|
||||
m_length += size;
|
||||
}
|
||||
void append_zeroes(size_t size) {
|
||||
resize(m_length + size);
|
||||
memset(m_data + m_length, 0, size);
|
||||
m_length += size;
|
||||
}
|
||||
|
||||
void resize(size_t size) {
|
||||
if (size == 0) {
|
||||
clear();
|
||||
} else if (m_data == nullptr) {
|
||||
m_data = static_cast<uint8_t*>(malloc(size));
|
||||
} else if (size > m_capacity) {
|
||||
m_data = static_cast<uint8_t*>(realloc(m_data, size));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
m_capacity = size;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (m_data != nullptr) {
|
||||
free(m_data);
|
||||
}
|
||||
m_data = nullptr;
|
||||
m_length = 0;
|
||||
m_capacity = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t* m_data = nullptr;
|
||||
size_t m_length = 0;
|
||||
size_t m_capacity = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Vec2 {
|
||||
T x{};
|
||||
T y{};
|
||||
};
|
||||
template <typename T>
|
||||
struct Vec3 {
|
||||
T x{};
|
||||
T y{};
|
||||
T z{};
|
||||
};
|
||||
template <typename T>
|
||||
struct Vec4 {
|
||||
T x{};
|
||||
T y{};
|
||||
T z{};
|
||||
T w{};
|
||||
};
|
||||
} // namespace aurora
|
||||
|
||||
namespace aurora::gfx {
|
||||
static logvisor::Module Log("aurora::gfx");
|
||||
|
||||
extern zeus::CMatrix4f g_mv;
|
||||
extern zeus::CMatrix4f g_mvInv;
|
||||
extern zeus::CMatrix4f g_proj;
|
||||
extern metaforce::CFogState g_fogState;
|
||||
|
||||
extern wgpu::Buffer g_vertexBuffer;
|
||||
extern wgpu::Buffer g_uniformBuffer;
|
||||
extern wgpu::Buffer g_indexBuffer;
|
||||
|
||||
struct TextureRef {
|
||||
wgpu::Texture texture;
|
||||
wgpu::TextureView view;
|
||||
@@ -38,11 +117,41 @@ struct TextureRef {
|
||||
};
|
||||
|
||||
using PipelineRef = uint64_t;
|
||||
using Range = std::pair<uint64_t, uint64_t>;
|
||||
using BindGroupRef = uint64_t;
|
||||
using Range = std::pair<uint32_t, uint32_t>;
|
||||
|
||||
enum class ShaderType {
|
||||
Aabb,
|
||||
TexturedQuad,
|
||||
MoviePlayer,
|
||||
};
|
||||
|
||||
void construct_state();
|
||||
|
||||
void render(const wgpu::RenderPassEncoder& pass);
|
||||
|
||||
Range push_verts(const uint8_t* data, size_t length);
|
||||
template <typename T>
|
||||
static inline Range push_verts(ArrayRef<T> data) {
|
||||
return push_verts(reinterpret_cast<const uint8_t*>(data.data()), data.size() * sizeof(T));
|
||||
}
|
||||
Range push_indices(const uint8_t* data, size_t length);
|
||||
template <typename T>
|
||||
static inline Range push_indices(ArrayRef<T> data) {
|
||||
return push_indices(reinterpret_cast<const uint8_t*>(data.data()), data.size() * sizeof(T));
|
||||
}
|
||||
Range push_uniform(const uint8_t* data, size_t length);
|
||||
template <typename T>
|
||||
static inline Range push_uniform(const T& data) {
|
||||
return push_uniform(reinterpret_cast<const uint8_t*>(&data), sizeof(T));
|
||||
}
|
||||
|
||||
template <typename PipelineConfig>
|
||||
PipelineRef pipeline_ref(PipelineConfig config);
|
||||
bool bind_pipeline(PipelineRef ref, const wgpu::RenderPassEncoder& pass);
|
||||
|
||||
BindGroupRef bind_group_ref(const wgpu::BindGroupDescriptor& descriptor);
|
||||
const wgpu::BindGroup& find_bind_group(BindGroupRef id);
|
||||
|
||||
static inline zeus::CMatrix4f get_combined_matrix() { return g_proj * g_mv; }
|
||||
} // namespace aurora::gfx
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
#include "shader.hpp"
|
||||
|
||||
#include "../../gpu.hpp"
|
||||
|
||||
namespace aurora::gfx::movie_player {
|
||||
using gpu::g_device;
|
||||
using gpu::g_graphicsConfig;
|
||||
using gpu::utils::make_vertex_attributes;
|
||||
using gpu::utils::make_vertex_buffer_layout;
|
||||
using gpu::utils::make_vertex_state;
|
||||
|
||||
State construct_state() {
|
||||
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
|
||||
wgslDescriptor.source = R"""(
|
||||
struct Uniform {
|
||||
xf: mat4x4<f32>;
|
||||
color: vec4<f32>;
|
||||
};
|
||||
@group(0) @binding(0)
|
||||
var<uniform> ubuf: Uniform;
|
||||
@group(0) @binding(1)
|
||||
var tex_sampler: sampler;
|
||||
@group(1) @binding(0)
|
||||
var tex_y: texture_2d<f32>;
|
||||
@group(1) @binding(1)
|
||||
var tex_u: texture_2d<f32>;
|
||||
@group(1) @binding(2)
|
||||
var tex_v: texture_2d<f32>;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) pos: vec4<f32>;
|
||||
@location(0) uv: vec2<f32>;
|
||||
};
|
||||
|
||||
@stage(vertex)
|
||||
fn vs_main(@location(0) in_pos: vec3<f32>, @location(1) in_uv: vec2<f32>) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.pos = ubuf.xf * vec4<f32>(in_pos, 1.0);
|
||||
out.uv = in_uv;
|
||||
return out;
|
||||
}
|
||||
|
||||
@stage(fragment)
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
var yuv = vec3<f32>(
|
||||
1.1643 * (textureSample(tex_y, tex_sampler, in.uv).x - 0.0625),
|
||||
textureSample(tex_u, tex_sampler, in.uv).x - 0.5,
|
||||
textureSample(tex_v, tex_sampler, in.uv).x - 0.5
|
||||
);
|
||||
return ubuf.color * vec4<f32>(
|
||||
yuv.x + 1.5958 * yuv.z,
|
||||
yuv.x - 0.39173 * yuv.y - 0.8129 * yuv.z,
|
||||
yuv.x + 2.017 * yuv.y,
|
||||
1.0
|
||||
);
|
||||
}
|
||||
)""";
|
||||
const auto shaderDescriptor = wgpu::ShaderModuleDescriptor{
|
||||
.nextInChain = &wgslDescriptor,
|
||||
.label = "Movie Player Shader",
|
||||
};
|
||||
auto shader = g_device.CreateShaderModule(&shaderDescriptor);
|
||||
|
||||
wgpu::SupportedLimits limits;
|
||||
g_device.GetLimits(&limits);
|
||||
const auto uniform_alignment = limits.limits.minUniformBufferOffsetAlignment;
|
||||
const auto uniform_size = ALIGN(sizeof(Uniform), uniform_alignment);
|
||||
|
||||
const std::array uniformLayoutEntries{
|
||||
wgpu::BindGroupLayoutEntry{
|
||||
.binding = 0,
|
||||
.visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
|
||||
.buffer =
|
||||
wgpu::BufferBindingLayout{
|
||||
.type = wgpu::BufferBindingType::Uniform,
|
||||
.hasDynamicOffset = true,
|
||||
.minBindingSize = uniform_size,
|
||||
},
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry{
|
||||
.binding = 1,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.sampler =
|
||||
wgpu::SamplerBindingLayout{
|
||||
.type = wgpu::SamplerBindingType::Filtering,
|
||||
},
|
||||
},
|
||||
};
|
||||
const auto uniformLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{
|
||||
.label = "Movie Player Uniform Bind Group Layout",
|
||||
.entryCount = uniformLayoutEntries.size(),
|
||||
.entries = uniformLayoutEntries.data(),
|
||||
};
|
||||
auto uniformLayout = g_device.CreateBindGroupLayout(&uniformLayoutDescriptor);
|
||||
|
||||
const auto samplerDescriptor = wgpu::SamplerDescriptor{
|
||||
.addressModeU = wgpu::AddressMode::Repeat,
|
||||
.addressModeV = wgpu::AddressMode::Repeat,
|
||||
.addressModeW = wgpu::AddressMode::Repeat,
|
||||
.magFilter = wgpu::FilterMode::Linear,
|
||||
.minFilter = wgpu::FilterMode::Linear,
|
||||
.mipmapFilter = wgpu::FilterMode::Linear,
|
||||
.maxAnisotropy = g_graphicsConfig.textureAnistropy,
|
||||
};
|
||||
auto sampler = g_device.CreateSampler(&samplerDescriptor);
|
||||
|
||||
const std::array uniformBindGroupEntries{
|
||||
wgpu::BindGroupEntry{
|
||||
.binding = 0,
|
||||
.buffer = g_uniformBuffer,
|
||||
.offset = 0,
|
||||
.size = uniform_size,
|
||||
},
|
||||
wgpu::BindGroupEntry{
|
||||
.binding = 1,
|
||||
.sampler = sampler,
|
||||
},
|
||||
};
|
||||
const auto uniformBindGroupDescriptor = wgpu::BindGroupDescriptor{
|
||||
.label = "Movie Player Uniform Bind Group",
|
||||
.layout = uniformLayout,
|
||||
.entryCount = uniformBindGroupEntries.size(),
|
||||
.entries = uniformBindGroupEntries.data(),
|
||||
};
|
||||
auto uniformBindGroup = g_device.CreateBindGroup(&uniformBindGroupDescriptor);
|
||||
|
||||
const auto textureBinding = wgpu::TextureBindingLayout{
|
||||
.sampleType = wgpu::TextureSampleType::Float,
|
||||
.viewDimension = wgpu::TextureViewDimension::e2D,
|
||||
};
|
||||
const std::array textureLayoutEntries{
|
||||
wgpu::BindGroupLayoutEntry{
|
||||
.binding = 0,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.texture = textureBinding,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry{
|
||||
.binding = 1,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.texture = textureBinding,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry{
|
||||
.binding = 2,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.texture = textureBinding,
|
||||
},
|
||||
};
|
||||
const auto textureLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{
|
||||
.label = "Movie Player Texture Bind Group Layout",
|
||||
.entryCount = textureLayoutEntries.size(),
|
||||
.entries = textureLayoutEntries.data(),
|
||||
};
|
||||
auto textureLayout = g_device.CreateBindGroupLayout(&textureLayoutDescriptor);
|
||||
|
||||
const std::array bindGroupLayouts{
|
||||
uniformLayout,
|
||||
textureLayout,
|
||||
};
|
||||
const auto pipelineLayoutDescriptor = wgpu::PipelineLayoutDescriptor{
|
||||
.label = "Movie Player Pipeline Layout",
|
||||
.bindGroupLayoutCount = bindGroupLayouts.size(),
|
||||
.bindGroupLayouts = bindGroupLayouts.data(),
|
||||
};
|
||||
auto pipelineLayout = g_device.CreatePipelineLayout(&pipelineLayoutDescriptor);
|
||||
|
||||
return {
|
||||
.shader = shader,
|
||||
.uniformLayout = uniformLayout,
|
||||
.uniformBindGroup = uniformBindGroup,
|
||||
.textureLayout = textureLayout,
|
||||
.sampler = sampler,
|
||||
.pipelineLayout = pipelineLayout,
|
||||
};
|
||||
}
|
||||
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config) {
|
||||
const auto attributes =
|
||||
make_vertex_attributes(std::array{wgpu::VertexFormat::Float32x3, wgpu::VertexFormat::Float32x2});
|
||||
const std::array vertexBuffers{make_vertex_buffer_layout(sizeof(Vert), attributes)};
|
||||
const auto depthStencil = wgpu::DepthStencilState{
|
||||
.format = g_graphicsConfig.depthFormat,
|
||||
};
|
||||
const auto blendComponent = wgpu::BlendComponent{
|
||||
.srcFactor = wgpu::BlendFactor::SrcAlpha,
|
||||
.dstFactor = wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
};
|
||||
const auto blendState = wgpu::BlendState{
|
||||
.color = blendComponent,
|
||||
.alpha = blendComponent,
|
||||
};
|
||||
const std::array colorTargets{
|
||||
wgpu::ColorTargetState{
|
||||
.format = g_graphicsConfig.colorFormat,
|
||||
.blend = &blendState,
|
||||
.writeMask = wgpu::ColorWriteMask::Red | wgpu::ColorWriteMask::Green | wgpu::ColorWriteMask::Blue,
|
||||
},
|
||||
};
|
||||
const auto fragmentState = wgpu::FragmentState{
|
||||
.module = state.shader,
|
||||
.entryPoint = "fs_main",
|
||||
.targetCount = colorTargets.size(),
|
||||
.targets = colorTargets.data(),
|
||||
};
|
||||
const auto pipelineDescriptor = wgpu::RenderPipelineDescriptor{
|
||||
.label = "Movie Player Pipeline",
|
||||
.layout = state.pipelineLayout,
|
||||
.vertex = make_vertex_state(state.shader, vertexBuffers),
|
||||
.primitive =
|
||||
wgpu::PrimitiveState{
|
||||
.topology = wgpu::PrimitiveTopology::TriangleStrip,
|
||||
},
|
||||
.depthStencil = &depthStencil,
|
||||
.multisample =
|
||||
wgpu::MultisampleState{
|
||||
.count = g_graphicsConfig.msaaSamples,
|
||||
},
|
||||
.fragment = &fragmentState,
|
||||
};
|
||||
return g_device.CreateRenderPipeline(&pipelineDescriptor);
|
||||
}
|
||||
|
||||
DrawData make_draw_data(const State& state, const TextureHandle& tex_y, const TextureHandle& tex_u,
|
||||
const TextureHandle& tex_v, const zeus::CColor& color, float h_pad, float v_pad) {
|
||||
auto pipeline = pipeline_ref(PipelineConfig{});
|
||||
|
||||
const std::array<Vert, 4> verts{
|
||||
Vert{{-h_pad, v_pad, 0.f}, {0.0, 0.0}},
|
||||
Vert{{-h_pad, -v_pad, 0.f}, {0.0, 1.0}},
|
||||
Vert{{h_pad, v_pad, 0.f}, {1.0, 0.0}},
|
||||
Vert{{h_pad, -v_pad, 0.f}, {1.0, 1.0}},
|
||||
};
|
||||
const auto vertRange = push_verts(ArrayRef{verts});
|
||||
|
||||
const auto uniform = Uniform{
|
||||
.xf = zeus::CMatrix4f{},
|
||||
.color = color,
|
||||
};
|
||||
const auto uniformRange = push_uniform(uniform);
|
||||
|
||||
std::array<wgpu::BindGroupEntry, 3> entries{
|
||||
wgpu::BindGroupEntry{
|
||||
.binding = 0,
|
||||
.textureView = tex_y.ref->view,
|
||||
},
|
||||
wgpu::BindGroupEntry{
|
||||
.binding = 1,
|
||||
.textureView = tex_u.ref->view,
|
||||
},
|
||||
wgpu::BindGroupEntry{
|
||||
.binding = 2,
|
||||
.textureView = tex_v.ref->view,
|
||||
},
|
||||
};
|
||||
const auto textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
|
||||
.label = "Movie Player Texture Bind Group",
|
||||
.layout = state.textureLayout,
|
||||
.entryCount = entries.size(),
|
||||
.entries = entries.data(),
|
||||
});
|
||||
|
||||
return {
|
||||
.pipeline = pipeline,
|
||||
.vertRange = vertRange,
|
||||
.uniformRange = uniformRange,
|
||||
.textureBindGroup = textureBindGroup,
|
||||
};
|
||||
}
|
||||
|
||||
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass) {
|
||||
if (!bind_pipeline(data.pipeline, pass)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::array offsets{data.uniformRange.first};
|
||||
pass.SetBindGroup(0, state.uniformBindGroup, offsets.size(), offsets.data());
|
||||
pass.SetBindGroup(1, find_bind_group(data.textureBindGroup));
|
||||
pass.SetVertexBuffer(0, g_vertexBuffer, data.vertRange.first, data.vertRange.second);
|
||||
pass.Draw(4);
|
||||
}
|
||||
} // namespace aurora::gfx::movie_player
|
||||
|
||||
@@ -5,13 +5,13 @@ struct DrawData {
|
||||
PipelineRef pipeline;
|
||||
Range vertRange;
|
||||
Range uniformRange;
|
||||
uint64_t bindGroupId;
|
||||
BindGroupRef textureBindGroup;
|
||||
};
|
||||
|
||||
struct PipelineConfig {
|
||||
// nothing
|
||||
};
|
||||
const std::array INITIAL_PIPELINES{
|
||||
static const std::array INITIAL_PIPELINES{
|
||||
PipelineConfig{},
|
||||
};
|
||||
|
||||
@@ -22,8 +22,20 @@ struct State {
|
||||
wgpu::BindGroupLayout textureLayout;
|
||||
wgpu::Sampler sampler;
|
||||
wgpu::PipelineLayout pipelineLayout;
|
||||
// Transient state
|
||||
std::unordered_map<uint64_t, wgpu::BindGroup> textureBindGroups;
|
||||
std::vector<uint64_t> frameUsedTextures;
|
||||
};
|
||||
|
||||
struct Vert {
|
||||
Vec3<float> pos;
|
||||
Vec2<float> uv;
|
||||
};
|
||||
struct Uniform {
|
||||
zeus::CMatrix4f xf;
|
||||
zeus::CColor color;
|
||||
};
|
||||
|
||||
State construct_state();
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config);
|
||||
DrawData make_draw_data(const State& state, const TextureHandle& tex_y, const TextureHandle& tex_u,
|
||||
const TextureHandle& tex_v, const zeus::CColor& color, float h_pad, float v_pad);
|
||||
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass);
|
||||
} // namespace aurora::gfx::movie_player
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
#include "common.hpp"
|
||||
|
||||
#include "../gpu.hpp"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
namespace aurora::gfx {
|
||||
static logvisor::Module Log("aurora::gfx");
|
||||
|
||||
using gpu::g_device;
|
||||
using gpu::g_queue;
|
||||
|
||||
static wgpu::TextureFormat to_wgpu(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::RGBA8:
|
||||
@@ -77,7 +84,7 @@ TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mi
|
||||
.bytesPerRow = bytesPerRow,
|
||||
.rowsPerImage = heightBlocks,
|
||||
};
|
||||
g_Queue.WriteTexture(&dstView, data.data() + offset, dataSize, &dataLayout, &physicalSize);
|
||||
g_queue.WriteTexture(&dstView, data.data() + offset, dataSize, &dataLayout, &physicalSize);
|
||||
offset += dataSize;
|
||||
}
|
||||
if (offset < data.size()) {
|
||||
@@ -108,7 +115,7 @@ TextureHandle new_dynamic_texture_2d(uint32_t width, uint32_t height, uint32_t m
|
||||
.dimension = wgpu::TextureViewDimension::e2D,
|
||||
.mipLevelCount = mips,
|
||||
};
|
||||
auto texture = g_Device.CreateTexture(&textureDescriptor);
|
||||
auto texture = g_device.CreateTexture(&textureDescriptor);
|
||||
auto textureView = texture.CreateView(&textureViewDescriptor);
|
||||
return {std::make_shared<TextureRef>(std::move(texture), std::move(textureView), size, wgpuFormat)};
|
||||
}
|
||||
@@ -142,6 +149,6 @@ void write_texture(const TextureHandle& handle, ArrayRef<uint8_t> data) noexcept
|
||||
.bytesPerRow = bytesPerRow,
|
||||
.rowsPerImage = heightBlocks,
|
||||
};
|
||||
g_Queue.WriteTexture(&dstView, data.data(), dataSize, &dataLayout, &ref.size);
|
||||
g_queue.WriteTexture(&dstView, data.data(), dataSize, &dataLayout, &ref.size);
|
||||
}
|
||||
} // namespace aurora::gfx
|
||||
|
||||
Reference in New Issue
Block a user