beetle-psx-libretro/parallel-psx/atlas/atlas.hpp

263 lines
6.9 KiB
C++

#pragma once
#include <stdint.h>
#include <vector>
#include <algorithm>
namespace PSX
{
static const unsigned FB_WIDTH = 1024;
static const unsigned FB_HEIGHT = 512;
static const unsigned BLOCK_WIDTH = 8;
static const unsigned BLOCK_HEIGHT = 8;
static const unsigned NUM_BLOCKS_X = FB_WIDTH / BLOCK_WIDTH;
static const unsigned NUM_BLOCKS_Y = FB_HEIGHT / BLOCK_HEIGHT;
enum class Domain : unsigned
{
Unscaled,
Scaled
};
enum class Stage : unsigned
{
Compute,
Transfer,
Fragment,
FragmentTexture
};
enum class TextureMode
{
None,
Palette4bpp,
Palette8bpp,
ABGR1555
};
struct Rect
{
unsigned x = 0;
unsigned y = 0;
unsigned width = 0;
unsigned height = 0;
Rect() = default;
Rect(unsigned x, unsigned y, unsigned width, unsigned height)
: x(x)
, y(y)
, width(width)
, height(height)
{
}
inline bool operator==(const Rect &rect) const
{
return x == rect.x && y == rect.y && width == rect.width && height == rect.height;
}
inline bool operator!=(const Rect &rect) const
{
return x != rect.x || y != rect.y || width != rect.width || height != rect.height;
}
inline bool contains(const Rect &rect) const
{
return x <= rect.x && y <= rect.y && (x + width) >= (rect.x + rect.width) &&
(y + height) >= (rect.y + rect.height);
}
inline bool intersects(const Rect &rect) const
{
unsigned xend = std::min(x + width, rect.x + rect.width);
unsigned xbegin = std::max(x, rect.x);
unsigned yend = std::min(y + height, rect.y + rect.height);
unsigned ybegin = std::max(y, rect.y);
return xbegin < xend && ybegin < yend;
}
inline Rect scissor(const Rect &rect) const
{
unsigned x0 = std::max(x, rect.x);
unsigned y0 = std::max(y, rect.y);
unsigned x1 = std::min(x + width, rect.x + rect.width);
unsigned y1 = std::min(y + height, rect.y + rect.height);
unsigned width = std::max(int(x1) - int(x0), 0);
unsigned height = std::max(int(y1) - int(y0), 0);
return { x0, y0, width, height };
}
inline void extend_bounding_box(const Rect &rect)
{
unsigned x0 = std::min(x, rect.x);
unsigned y0 = std::min(y, rect.y);
unsigned x1 = std::max(x + width, rect.x + rect.width);
unsigned y1 = std::max(y + height, rect.y + rect.height);
x = x0;
y = y0;
width = x1 - x0;
height = y1 - y0;
}
};
using FBColor = uint32_t;
static inline uint32_t fbcolor_to_rgba8(FBColor color)
{
// 3 LSBs are ignored.
return color & 0xfff8f8f8u;
}
static inline void fbcolor_to_rgba32f(float *v, FBColor color)
{
// 3 LSBs are ignored.
unsigned r = (color >> 0) & 0xf8;
unsigned g = (color >> 8) & 0xf8;
unsigned b = (color >> 16) & 0xf8;
v[0] = r * (1.0f / 255.0f);
v[1] = g * (1.0f / 255.0f);
v[2] = b * (1.0f / 255.0f);
// Mask bit is always cleared.
v[3] = 0.0f;
}
enum StatusFlag
{
STATUS_FB_ONLY = 0,
STATUS_FB_PREFER = 1,
STATUS_SFB_ONLY = 2,
STATUS_SFB_PREFER = 3,
STATUS_OWNERSHIP_MASK = 3,
STATUS_COMPUTE_FB_READ = 1 << 2,
STATUS_COMPUTE_FB_WRITE = 1 << 3,
STATUS_COMPUTE_SFB_READ = 1 << 4,
STATUS_COMPUTE_SFB_WRITE = 1 << 5,
STATUS_TRANSFER_FB_READ = 1 << 6,
STATUS_TRANSFER_SFB_READ = 1 << 7,
STATUS_TRANSFER_FB_WRITE = 1 << 8,
STATUS_TRANSFER_SFB_WRITE = 1 << 9,
STATUS_FRAGMENT_SFB_READ = 1 << 10,
STATUS_FRAGMENT_SFB_WRITE = 1 << 11,
STATUS_FRAGMENT_FB_READ = 1 << 12,
STATUS_FRAGMENT_FB_WRITE = 1 << 13,
// A special stage to allow fragment to detect when it's causing a feedback loop with texture read -> fragment write.
// This flag is added in combination with FRAGMENT_FB_READ.
STATUS_TEXTURE_READ = 1 << 14,
// For determining if a texture read is from a loaded image or previous rendered content
STATUS_TEXTURE_RENDERED = 1 << 15,
STATUS_FB_READ = STATUS_COMPUTE_FB_READ | STATUS_TRANSFER_FB_READ | STATUS_FRAGMENT_FB_READ,
STATUS_FB_WRITE = STATUS_COMPUTE_FB_WRITE | STATUS_TRANSFER_FB_WRITE | STATUS_FRAGMENT_FB_WRITE,
STATUS_SFB_READ = STATUS_COMPUTE_SFB_READ | STATUS_TRANSFER_SFB_READ | STATUS_FRAGMENT_SFB_READ,
STATUS_SFB_WRITE = STATUS_COMPUTE_SFB_WRITE | STATUS_TRANSFER_SFB_WRITE | STATUS_FRAGMENT_SFB_WRITE,
STATUS_FRAGMENT =
STATUS_FRAGMENT_FB_READ | STATUS_FRAGMENT_FB_WRITE | STATUS_FRAGMENT_SFB_READ | STATUS_FRAGMENT_SFB_WRITE,
STATUS_ALL = STATUS_FB_READ | STATUS_FB_WRITE | STATUS_SFB_READ | STATUS_SFB_WRITE
};
using StatusFlags = uint16_t;
class HazardListener
{
public:
virtual ~HazardListener() = default;
virtual void hazard(StatusFlags flags) = 0;
virtual void resolve(Domain target_domain, unsigned x, unsigned y) = 0;
virtual void flush_render_pass(const Rect &rect) = 0;
virtual void discard_render_pass() = 0;
virtual void clear_quad(const Rect &rect, FBColor color, bool clear_candidate) = 0;
virtual void set_scissored_invariant(bool invariant) = 0;
};
class FBAtlas
{
public:
FBAtlas();
void set_hazard_listener(HazardListener *hazard)
{
listener = hazard;
}
void read_compute(Domain domain, const Rect &rect);
void write_compute(Domain domain, const Rect &rect);
void read_transfer(Domain domain, const Rect &rect);
void write_transfer(Domain domain, const Rect &rect);
void read_fragment(Domain domain, const Rect &rect);
Domain blit_vram(const Rect &dst, const Rect &src);
void load_image(const Rect &rect);
bool texture_rendered(const Rect &rect);
void write_fragment(Domain domain, const Rect &rect);
void clear_rect(const Rect &rect, FBColor color);
void set_draw_rect(const Rect &rect);
void set_texture_window(const Rect &rect);
TextureMode set_texture_mode(TextureMode mode)
{
std::swap(renderpass.texture_mode, mode);
return mode;
}
void set_texture_offset(unsigned x, unsigned y)
{
renderpass.texture_offset_x = x;
renderpass.texture_offset_y = y;
}
void set_palette_offset(unsigned x, unsigned y)
{
renderpass.palette_offset_x = x;
renderpass.palette_offset_y = y;
}
void pipeline_barrier(StatusFlags domains);
void notify_external_barrier(StatusFlags domains);
void flush_render_pass();
private:
StatusFlags fb_info[NUM_BLOCKS_X * NUM_BLOCKS_Y];
HazardListener *listener = nullptr;
void read_domain(Domain domain, Stage stage, const Rect &rect);
bool write_domain(Domain domain, Stage stage, const Rect &rect);
void sync_domain(Domain domain, const Rect &rect);
void read_texture(Domain domain);
Domain find_suitable_domain(const Rect &rect);
struct
{
Rect rect;
Rect scissor;
Rect texture_window;
unsigned texture_offset_x = 0, texture_offset_y = 0;
unsigned palette_offset_x = 0, palette_offset_y = 0;
TextureMode texture_mode = TextureMode::None;
bool inside = false;
} renderpass;
void extend_render_pass(const Rect &rect, bool scissor);
StatusFlags &info(unsigned block_x, unsigned block_y)
{
block_x &= NUM_BLOCKS_X - 1;
block_y &= NUM_BLOCKS_Y - 1;
return fb_info[NUM_BLOCKS_X * block_y + block_x];
}
const StatusFlags &info(unsigned block_x, unsigned block_y) const
{
block_x &= NUM_BLOCKS_X - 1;
block_y &= NUM_BLOCKS_Y - 1;
return fb_info[NUM_BLOCKS_X * block_y + block_x];
}
void discard_render_pass();
bool inside_render_pass(const Rect &rect);
};
}