Skip to content

Commit

Permalink
lovr.headset.setFoveation;
Browse files Browse the repository at this point in the history
  • Loading branch information
bjornbytes committed Dec 17, 2024
1 parent a3179fe commit 2558665
Show file tree
Hide file tree
Showing 10 changed files with 289 additions and 34 deletions.
1 change: 1 addition & 0 deletions src/api/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ extern StringEntry lovrEffect[];
extern StringEntry lovrEventType[];
extern StringEntry lovrFileAction[];
extern StringEntry lovrFilterMode[];
extern StringEntry lovrFoveationLevel[];
extern StringEntry lovrHeadsetDriver[];
extern StringEntry lovrHorizontalAlign[];
extern StringEntry lovrJointType[];
Expand Down
5 changes: 3 additions & 2 deletions src/api/l_graphics_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ static int l_lovrPassGetCanvas(lua_State* L) {
CanvasTexture color[4];
CanvasTexture depth;
uint32_t depthFormat;
Texture* foveation;
uint32_t samples;
lovrPassGetCanvas(pass, color, &depth, &depthFormat, &samples);
lovrPassGetCanvas(pass, color, &depth, &depthFormat, &foveation, &samples);

if (!color[0].texture && !depth.texture) {
lua_pushnil(L);
Expand Down Expand Up @@ -141,7 +142,7 @@ int l_lovrPassSetCanvas(lua_State* L) {
} else if (!lua_isnoneornil(L, 2)) {
luaL_error(L, "Expected Texture, table, or nil for canvas");
}
luax_assert(L, lovrPassSetCanvas(pass, color, &depth, depthFormat, samples));
luax_assert(L, lovrPassSetCanvas(pass, color, &depth, depthFormat, NULL, samples));
return 0;
}

Expand Down
33 changes: 33 additions & 0 deletions src/api/l_headset.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ StringEntry lovrControllerSkeletonMode[] = {
{ 0 }
};

StringEntry lovrFoveationLevel[] = {
[FOVEATION_NONE] = ENTRY("none"),
[FOVEATION_LOW] = ENTRY("low"),
[FOVEATION_MEDIUM] = ENTRY("medium"),
[FOVEATION_HIGH] = ENTRY("high"),
{ 0 }
};

StringEntry lovrPassthroughMode[] = {
[PASSTHROUGH_OPAQUE] = ENTRY("opaque"),
[PASSTHROUGH_BLEND] = ENTRY("blend"),
Expand Down Expand Up @@ -218,6 +226,29 @@ static int l_lovrHeadsetGetRefreshRates(lua_State* L) {
return 1;
}

static int l_lovrHeadsetGetFoveation(lua_State* L) {
FoveationLevel level;
bool dynamic;
lovrHeadsetInterface->getFoveation(&level, &dynamic);
luax_pushenum(L, FoveationLevel, level);
lua_pushboolean(L, dynamic);
return 2;
}

static int l_lovrHeadsetSetFoveation(lua_State* L) {
if (lua_isnoneornil(L, 1)) {
bool success = lovrHeadsetInterface->setFoveation(FOVEATION_NONE, false);
lua_pushboolean(L, success);
return 1;
} else {
FoveationLevel level = luax_checkenum(L, 1, FoveationLevel, NULL);
bool dynamic = lua_isnoneornil(L, -1) ? true : lua_toboolean(L, 2);
bool success = lovrHeadsetInterface->setFoveation(level, dynamic);
lua_pushboolean(L, success);
return 1;
}
}

static int l_lovrHeadsetGetPassthrough(lua_State* L) {
PassthroughMode mode = lovrHeadsetInterface->getPassthrough();
luax_pushenum(L, PassthroughMode, mode);
Expand Down Expand Up @@ -895,6 +926,8 @@ static const luaL_Reg lovrHeadset[] = {
{ "getRefreshRate", l_lovrHeadsetGetRefreshRate },
{ "setRefreshRate", l_lovrHeadsetSetRefreshRate },
{ "getRefreshRates", l_lovrHeadsetGetRefreshRates },
{ "getFoveation", l_lovrHeadsetGetFoveation },
{ "setFoveation", l_lovrHeadsetSetFoveation },
{ "getPassthrough", l_lovrHeadsetGetPassthrough },
{ "setPassthrough", l_lovrHeadsetSetPassthrough },
{ "getPassthroughModes", l_lovrHeadsetGetPassthroughModes },
Expand Down
6 changes: 5 additions & 1 deletion src/core/gpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ enum {
GPU_TEXTURE_RENDER = (1 << 1),
GPU_TEXTURE_STORAGE = (1 << 2),
GPU_TEXTURE_COPY_SRC = (1 << 3),
GPU_TEXTURE_COPY_DST = (1 << 4)
GPU_TEXTURE_COPY_DST = (1 << 4),
GPU_TEXTURE_FOVEATION = (1 << 5)
};

typedef enum {
Expand Down Expand Up @@ -348,6 +349,7 @@ typedef struct {
uint32_t colorCount;
uint32_t samples;
uint32_t views;
bool foveated;
bool surface;
} gpu_pass_info;

Expand Down Expand Up @@ -579,6 +581,7 @@ typedef struct {
typedef struct {
gpu_color_attachment color[4];
gpu_depth_attachment depth;
gpu_texture* foveation;
gpu_pass* pass;
uint32_t width;
uint32_t height;
Expand Down Expand Up @@ -690,6 +693,7 @@ typedef struct {
bool wireframe;
bool depthClamp;
bool depthResolve;
bool foveation;
bool indirectDrawFirstInstance;
bool packedBuffers;
bool shaderDebug;
Expand Down
56 changes: 52 additions & 4 deletions src/core/gpu_vk.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ typedef struct {
bool renderPass2;
bool synchronization2;
bool scalarBlockLayout;
bool fdExternalMemory;
bool foveation;
} gpu_extensions;

// State
Expand Down Expand Up @@ -486,6 +488,7 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
((info->usage & GPU_TEXTURE_STORAGE) ? VK_IMAGE_USAGE_STORAGE_BIT : 0) |
((info->usage & GPU_TEXTURE_COPY_SRC) ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 0) |
((info->usage & GPU_TEXTURE_COPY_DST) ? VK_IMAGE_USAGE_TRANSFER_DST_BIT : 0) |
((info->usage & GPU_TEXTURE_FOVEATION) ? VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT : 0) |
((info->usage == GPU_TEXTURE_RENDER) ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0) |
(info->upload.levelCount > 0 ? VK_IMAGE_USAGE_TRANSFER_DST_BIT : 0) |
(info->upload.generateMipmaps ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 0)
Expand Down Expand Up @@ -697,7 +700,8 @@ bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info) {
((info->usage & GPU_TEXTURE_SAMPLE) ? VK_IMAGE_USAGE_SAMPLED_BIT : 0) |
(((info->usage & GPU_TEXTURE_RENDER) && texture->aspect == VK_IMAGE_ASPECT_COLOR_BIT) ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : 0) |
(((info->usage & GPU_TEXTURE_RENDER) && texture->aspect != VK_IMAGE_ASPECT_COLOR_BIT) ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : 0) |
((info->usage & GPU_TEXTURE_STORAGE) && !texture->srgb ? VK_IMAGE_USAGE_STORAGE_BIT : 0)
((info->usage & GPU_TEXTURE_STORAGE) && !texture->srgb ? VK_IMAGE_USAGE_STORAGE_BIT : 0) |
((info->usage & GPU_TEXTURE_FOVEATION) ? VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT : 0)
};

if (viewUsage.usage == 0) {
Expand Down Expand Up @@ -1413,6 +1417,18 @@ bool gpu_pass_init(gpu_pass* pass, gpu_pass_info* info) {
}
}

if (info->foveated) {
attachments[attachmentCount++] = (VkAttachmentDescription2) {
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
.format = VK_FORMAT_R8G8_UNORM,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT,
.finalLayout = VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT
};
}

uint32_t referenceCount = (info->colorCount << hasColorResolve) + (depth << info->depth.resolve);

VkSubpassDescription2 subpass = {
Expand All @@ -1432,6 +1448,13 @@ bool gpu_pass_init(gpu_pass* pass, gpu_pass_info* info) {

VkRenderPassCreateInfo2 createInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
.pNext = info->foveated ? &(VkRenderPassFragmentDensityMapCreateInfoEXT) {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT,
.fragmentDensityMapAttachment = {
.attachment = attachmentCount - 1,
.layout = VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT
}
} : NULL,
.attachmentCount = attachmentCount,
.pAttachments = attachments,
.subpassCount = 1,
Expand Down Expand Up @@ -1860,8 +1883,8 @@ void gpu_render_begin(gpu_stream* stream, gpu_canvas* canvas) {

// Framebuffer

VkImageView images[10];
VkClearValue clears[10];
VkImageView images[11];
VkClearValue clears[11];
uint32_t attachmentCount = 0;

for (uint32_t i = 0; i < pass->colorCount; i++) {
Expand All @@ -1885,6 +1908,11 @@ void gpu_render_begin(gpu_stream* stream, gpu_canvas* canvas) {
}
}

if (canvas->foveation) {
uint32_t index = attachmentCount++;
images[index] = canvas->foveation->view;
}

VkFramebufferCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = pass->handle,
Expand Down Expand Up @@ -2386,7 +2414,9 @@ bool gpu_init(gpu_config* config) {
{ "VK_KHR_shader_non_semantic_info", config->debug, &state.extensions.shaderDebug },
{ "VK_KHR_image_format_list", true, &state.extensions.formatList },
{ "VK_KHR_synchronization2", true, &state.extensions.synchronization2 },
{ "VK_EXT_scalar_block_layout", true, &state.extensions.scalarBlockLayout }
{ "VK_KHR_external_memory_fd", true, &state.extensions.fdExternalMemory }, // Not used by us, Meta forgot to enable this and it spews errors
{ "VK_EXT_scalar_block_layout", true, &state.extensions.scalarBlockLayout },
{ "VK_EXT_fragment_density_map", true, &state.extensions.foveation }
};

uint32_t extensionCount = 0;
Expand Down Expand Up @@ -2471,6 +2501,10 @@ bool gpu_init(gpu_config* config) {

// Features

VkPhysicalDeviceFragmentDensityMapFeaturesEXT fragmentDensityMapFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT
};

VkPhysicalDeviceScalarBlockLayoutFeaturesEXT scalarBlockLayoutFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT
};
Expand Down Expand Up @@ -2498,6 +2532,12 @@ bool gpu_init(gpu_config* config) {
VkPhysicalDeviceFeatures2 features2 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };
VkPhysicalDeviceFeatures* enable = &enabledFeatures.features;
VkPhysicalDeviceFeatures* supports = &features2.features;

if (state.extensions.foveation) {
fragmentDensityMapFeatures.pNext = features2.pNext;
features2.pNext = &fragmentDensityMapFeatures;
}

vkGetPhysicalDeviceFeatures2(state.adapter, &features2);

// Required features
Expand Down Expand Up @@ -2537,6 +2577,14 @@ bool gpu_init(gpu_config* config) {
enabledFeatures.pNext = &scalarBlockLayoutFeatures;
}

if (state.extensions.foveation && fragmentDensityMapFeatures.fragmentDensityMap) {
fragmentDensityMapFeatures.fragmentDensityMapDynamic = false;
fragmentDensityMapFeatures.fragmentDensityMapNonSubsampledImages = true;
fragmentDensityMapFeatures.pNext = enabledFeatures.pNext;
enabledFeatures.pNext = &fragmentDensityMapFeatures;
config->features->foveation = true;
}

// Formats
for (uint32_t i = 0; i < GPU_FORMAT_COUNT; i++) {
for (int j = 0; j < 2; j++) {
Expand Down
21 changes: 16 additions & 5 deletions src/modules/graphics/graphics.c
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ typedef struct {
typedef struct {
Attachment color[4];
Attachment depth;
Texture* foveation;
uint32_t count;
uint32_t width;
uint32_t height;
Expand Down Expand Up @@ -1192,6 +1193,7 @@ static bool recordRenderPass(Pass* pass, gpu_stream* stream) {
target.pass = pass->gpu;
target.width = canvas->width;
target.height = canvas->height;
target.foveation = canvas->foveation ? canvas->foveation->gpu : NULL;

// Cameras

Expand Down Expand Up @@ -2406,7 +2408,8 @@ Texture* lovrTextureCreate(const TextureInfo* info) {
((info->usage & TEXTURE_SAMPLE) ? GPU_TEXTURE_SAMPLE : 0) |
((info->usage & TEXTURE_RENDER) ? GPU_TEXTURE_RENDER : 0) |
((info->usage & TEXTURE_STORAGE) ? GPU_TEXTURE_STORAGE : 0) |
(transfer ? GPU_TEXTURE_COPY_SRC | GPU_TEXTURE_COPY_DST : 0),
(transfer ? GPU_TEXTURE_COPY_SRC | GPU_TEXTURE_COPY_DST : 0) |
((info->usage & TEXTURE_FOVEATION) ? GPU_TEXTURE_FOVEATION : 0),
.srgb = srgb,
.handle = info->handle,
.label = info->label,
Expand Down Expand Up @@ -5790,7 +5793,7 @@ bool lovrGraphicsGetWindowPass(Pass** pass) {
lovrPassReset(state.windowPass);
memcpy(state.windowPass->canvas.color[0].clear, state.background, 4 * sizeof(float));
CanvasTexture color[4] = { [0].texture = window };
lovrPassSetCanvas(state.windowPass, color, NULL, state.depthFormat, state.config.antialias ? 4 : 1);
lovrPassSetCanvas(state.windowPass, color, NULL, state.depthFormat, NULL, state.config.antialias ? 4 : 1);
*pass = state.windowPass;
return true;
}
Expand Down Expand Up @@ -5916,18 +5919,19 @@ const char* lovrPassGetLabel(Pass* pass) {
return pass->label;
}

void lovrPassGetCanvas(Pass* pass, CanvasTexture color[4], CanvasTexture* depth, uint32_t* depthFormat, uint32_t* samples) {
void lovrPassGetCanvas(Pass* pass, CanvasTexture color[4], CanvasTexture* depth, uint32_t* depthFormat, Texture** foveation, uint32_t* samples) {
for (uint32_t i = 0; i < COUNTOF(pass->canvas.color); i++) {
color[i].texture = pass->canvas.color[i].texture;
color[i].resolve = pass->canvas.color[i].resolve;
}
depth->texture = pass->canvas.depth.texture;
depth->resolve = pass->canvas.depth.resolve;
*depthFormat = pass->canvas.depth.format;
*foveation = pass->canvas.foveation;
*samples = pass->canvas.samples;
}

bool lovrPassSetCanvas(Pass* pass, CanvasTexture color[4], CanvasTexture* depth, uint32_t depthFormat, uint32_t samples) {
bool lovrPassSetCanvas(Pass* pass, CanvasTexture color[4], CanvasTexture* depth, uint32_t depthFormat, Texture* foveation, uint32_t samples) {
Canvas* canvas = &pass->canvas;

for (uint32_t i = 0; i < canvas->count; i++) {
Expand All @@ -5943,6 +5947,9 @@ bool lovrPassSetCanvas(Pass* pass, CanvasTexture color[4], CanvasTexture* depth,
canvas->depth.resolve = NULL;
canvas->depth.format = 0;

lovrRelease(canvas->foveation, lovrTextureDestroy);
canvas->foveation = NULL;

canvas->count = 0;
canvas->width = 0;
canvas->height = 0;
Expand Down Expand Up @@ -6043,10 +6050,13 @@ bool lovrPassSetCanvas(Pass* pass, CanvasTexture color[4], CanvasTexture* depth,
canvas->depth.automsaa = true;
}

lovrRetain(foveation);
canvas->foveation = foveation;

pass->gpu = getPass(canvas);

if (!pass->gpu) {
return lovrPassSetCanvas(pass, NULL, NULL, 0, 0);
return lovrPassSetCanvas(pass, NULL, NULL, 0, NULL, 0);
}

lovrPassReset(pass);
Expand Down Expand Up @@ -8348,6 +8358,7 @@ static gpu_pass* getPass(Canvas* canvas) {
info.colorCount = canvas->count;
info.samples = canvas->samples;
info.views = canvas->views;
info.foveated = !!canvas->foveation;
info.surface = canvas->count > 0 && canvas->color[0].texture == state.window;

uint64_t hash = hash64(&info, sizeof(info));
Expand Down
13 changes: 7 additions & 6 deletions src/modules/graphics/graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,11 @@ typedef enum {
} TextureType;

enum {
TEXTURE_SAMPLE = (1 << 0),
TEXTURE_RENDER = (1 << 1),
TEXTURE_STORAGE = (1 << 2),
TEXTURE_TRANSFER = (1 << 3)
TEXTURE_SAMPLE = (1 << 0),
TEXTURE_RENDER = (1 << 1),
TEXTURE_STORAGE = (1 << 2),
TEXTURE_TRANSFER = (1 << 3),
TEXTURE_FOVEATION = (1 << 4)
};

typedef struct {
Expand Down Expand Up @@ -580,8 +581,8 @@ void lovrPassReset(Pass* pass);
const PassStats* lovrPassGetStats(Pass* pass);
const char* lovrPassGetLabel(Pass* pass);

void lovrPassGetCanvas(Pass* pass, CanvasTexture color[4], CanvasTexture* depth, uint32_t* depthFormat, uint32_t* samples);
bool lovrPassSetCanvas(Pass* pass, CanvasTexture color[4], CanvasTexture* depth, uint32_t depthFormat, uint32_t samples);
void lovrPassGetCanvas(Pass* pass, CanvasTexture color[4], CanvasTexture* depth, uint32_t* depthFormat, Texture** foveation, uint32_t* samples);
bool lovrPassSetCanvas(Pass* pass, CanvasTexture color[4], CanvasTexture* depth, uint32_t depthFormat, Texture* foveation, uint32_t samples);
void lovrPassGetClear(Pass* pass, LoadAction loads[4], float clears[4][4], LoadAction* depthLoad, float* depthClear);
bool lovrPassSetClear(Pass* pass, LoadAction loads[4], float clears[4][4], LoadAction depthLoad, float depthClear);
uint32_t lovrPassGetAttachmentCount(Pass* pass, bool* depth);
Expand Down
9 changes: 9 additions & 0 deletions src/modules/headset/headset.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ typedef struct {
bool layerFilter;
} HeadsetFeatures;

typedef enum {
FOVEATION_NONE,
FOVEATION_LOW,
FOVEATION_MEDIUM,
FOVEATION_HIGH
} FoveationLevel;

typedef enum {
PASSTHROUGH_OPAQUE,
PASSTHROUGH_BLEND,
Expand Down Expand Up @@ -205,6 +212,8 @@ typedef struct HeadsetInterface {
float (*getRefreshRate)(void);
bool (*setRefreshRate)(float refreshRate);
const float* (*getRefreshRates)(uint32_t* count);
void (*getFoveation)(FoveationLevel* level, bool* dynamic);
bool (*setFoveation)(FoveationLevel level, bool dynamic);
PassthroughMode (*getPassthrough)(void);
bool (*setPassthrough)(PassthroughMode mode);
bool (*isPassthroughSupported)(PassthroughMode mode);
Expand Down
Loading

0 comments on commit 2558665

Please sign in to comment.