Skip to content

Commit 24bfc3a

Browse files
committed
Add video low pass filter. Fix #84
1 parent e2f4b28 commit 24bfc3a

File tree

10 files changed

+257
-6
lines changed

10 files changed

+257
-6
lines changed

platforms/libretro/libretro.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ static bool allow_up_down = false;
6666
static bool allow_soft_reset = false;
6767
static int cdrom_bios = 0;
6868
static bool deterministic_netplay = false;
69+
static bool lowpass_filter = false;
70+
static float lowpass_intensity = 1.0f;
71+
static float lowpass_cutoff_mhz = 5.0f;
72+
static bool lowpass_speed_536 = false;
73+
static bool lowpass_speed_716 = true;
74+
static bool lowpass_speed_108 = true;
6975

7076
static bool input_updated = false;
7177
static bool libretro_supports_bitmasks;
@@ -648,6 +654,12 @@ static void set_variabless(void)
648654
{ "geargrafx_scanline_start", "Scanline Start (Manual); 3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|0|1|2" },
649655
{ "geargrafx_scanline_end", "Scanline End (Manual); 241|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240" },
650656
{ "geargrafx_composite_colors", "Composite Colors; Disabled|Enabled" },
657+
{ "geargrafx_lowpass_filter", "Video LPF; Disabled|Enabled" },
658+
{ "geargrafx_lowpass_intensity", "Video LPF Intensity; 100|0|10|20|30|40|50|60|70|80|90|100" },
659+
{ "geargrafx_lowpass_cutoff", "Video LPF Cutoff; 5.0 MHz|3.0 MHz|3.5 MHz|4.0 MHz|4.5 MHz|5.0 MHz|5.5 MHz|6.0 MHz|6.5 MHz|7.0 MHz" },
660+
{ "geargrafx_lowpass_speed_536", "Video LPF HuC6270 5.36 MHz; Disabled|Enabled" },
661+
{ "geargrafx_lowpass_speed_716", "Video LPF HuC6270 7.16 MHz; Enabled|Disabled" },
662+
{ "geargrafx_lowpass_speed_108", "Video LPF HuC6270 10.8 MHz; Enabled|Disabled" },
651663
{ "geargrafx_no_sprite_limit", "No Sprite Limit; Disabled|Enabled" },
652664
{ "geargrafx_backup_ram", "Backup RAM (restart); Enabled|Disabled" },
653665
{ "geargrafx_deterministic_netplay", "Deterministic Netplay; Disabled|Enabled" },
@@ -800,6 +812,62 @@ static void check_variables(void)
800812
core->GetHuC6260()->SetPalette(strcmp(var.value, "Enabled") == 0 ? 1 : 0);
801813
}
802814

815+
var.key = "geargrafx_lowpass_filter";
816+
var.value = NULL;
817+
818+
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
819+
{
820+
lowpass_filter = (strcmp(var.value, "Enabled") == 0);
821+
}
822+
823+
var.key = "geargrafx_lowpass_intensity";
824+
var.value = NULL;
825+
826+
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
827+
{
828+
int intensity = atoi(var.value);
829+
if (intensity < 0 || intensity > 100)
830+
intensity = 100;
831+
lowpass_intensity = (float)intensity / 100.0f;
832+
}
833+
834+
var.key = "geargrafx_lowpass_cutoff";
835+
var.value = NULL;
836+
837+
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
838+
{
839+
lowpass_cutoff_mhz = (float)atof(var.value);
840+
if (lowpass_cutoff_mhz < 3.0f || lowpass_cutoff_mhz > 7.0f)
841+
lowpass_cutoff_mhz = 5.0f;
842+
}
843+
844+
var.key = "geargrafx_lowpass_speed_536";
845+
var.value = NULL;
846+
847+
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
848+
{
849+
lowpass_speed_536 = (strcmp(var.value, "Enabled") == 0);
850+
}
851+
852+
var.key = "geargrafx_lowpass_speed_716";
853+
var.value = NULL;
854+
855+
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
856+
{
857+
lowpass_speed_716 = (strcmp(var.value, "Enabled") == 0);
858+
}
859+
860+
var.key = "geargrafx_lowpass_speed_108";
861+
var.value = NULL;
862+
863+
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
864+
{
865+
lowpass_speed_108 = (strcmp(var.value, "Enabled") == 0);
866+
}
867+
868+
core->GetHuC6260()->SetLowPassFilter(lowpass_filter, lowpass_intensity, lowpass_cutoff_mhz,
869+
lowpass_speed_536, lowpass_speed_716, lowpass_speed_108);
870+
803871
var.key = "geargrafx_backup_ram";
804872
var.value = NULL;
805873

platforms/shared/desktop/config.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,12 @@ void config_read(void)
311311
config_video.scanlines = read_bool("Video", "Scanlines", true);
312312
config_video.scanlines_filter = read_bool("Video", "ScanlinesFilter", true);
313313
config_video.scanlines_intensity = read_float("Video", "ScanlinesIntensity", 0.10f);
314+
config_video.lowpass_filter = read_bool("Video", "LowpassFilter", false);
315+
config_video.lowpass_intensity = read_float("Video", "LowpassIntensity", 1.0f);
316+
config_video.lowpass_cutoff_mhz = read_float("Video", "LowpassCutoffMhz", 5.0f);
317+
config_video.lowpass_speed[0] = read_bool("Video", "LowpassSpeed536", false);
318+
config_video.lowpass_speed[1] = read_bool("Video", "LowpassSpeed716", true);
319+
config_video.lowpass_speed[2] = read_bool("Video", "LowpassSpeed108", true);
314320
config_video.sync = read_bool("Video", "Sync", true);
315321
config_video.background_color[0] = read_float("Video", "BackgroundColorR", 0.1f);
316322
config_video.background_color[1] = read_float("Video", "BackgroundColorG", 0.1f);
@@ -564,6 +570,12 @@ void config_write(void)
564570
write_bool("Video", "Scanlines", config_video.scanlines);
565571
write_bool("Video", "ScanlinesFilter", config_video.scanlines_filter);
566572
write_float("Video", "ScanlinesIntensity", config_video.scanlines_intensity);
573+
write_bool("Video", "LowpassFilter", config_video.lowpass_filter);
574+
write_float("Video", "LowpassIntensity", config_video.lowpass_intensity);
575+
write_float("Video", "LowpassCutoffMhz", config_video.lowpass_cutoff_mhz);
576+
write_bool("Video", "LowpassSpeed536", config_video.lowpass_speed[0]);
577+
write_bool("Video", "LowpassSpeed716", config_video.lowpass_speed[1]);
578+
write_bool("Video", "LowpassSpeed108", config_video.lowpass_speed[2]);
567579
write_bool("Video", "Sync", config_video.sync);
568580
write_float("Video", "BackgroundColorR", config_video.background_color[0]);
569581
write_float("Video", "BackgroundColorG", config_video.background_color[1]);

platforms/shared/desktop/config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ struct config_Video
9393
bool sync = true;
9494
float background_color[3] = {0.1f, 0.1f, 0.1f};
9595
float background_color_debugger[3] = {0.2f, 0.2f, 0.2f};
96+
bool lowpass_filter = false;
97+
float lowpass_intensity = 1.0f;
98+
float lowpass_cutoff_mhz = 5.0f;
99+
bool lowpass_speed[3] = { false, true, true };
96100
};
97101

98102
struct config_Audio

platforms/shared/desktop/emu.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,11 @@ void emu_set_scanline_start_end(int start, int end)
459459
geargrafx->GetHuC6260()->SetScanlineEnd(end);
460460
}
461461

462+
void emu_set_lowpass_filter(bool enabled, float intensity, float cutoff_mhz, bool speed_5_36, bool speed_7_16, bool speed_10_8)
463+
{
464+
geargrafx->GetHuC6260()->SetLowPassFilter(enabled, intensity, cutoff_mhz, speed_5_36, speed_7_16, speed_10_8);
465+
}
466+
462467
void emu_set_memory_reset_values(int mpr, int wram, int card_ram, int arcade_card)
463468
{
464469
geargrafx->GetMemory()->SetResetValues(mpr, wram, card_ram, arcade_card);

platforms/shared/desktop/emu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ EXTERN void emu_set_custom_palette(const u8* data);
102102
EXTERN void emu_video_no_sprite_limit(bool enabled);
103103
EXTERN void emu_set_overscan(int overscan);
104104
EXTERN void emu_set_scanline_start_end(int start, int end);
105+
EXTERN void emu_set_lowpass_filter(bool enabled, float intensity, float cutoff_mhz, bool speed_5_36, bool speed_7_16, bool speed_10_8);
105106
EXTERN void emu_set_memory_reset_values(int mpr, int wram, int card_ram, int arcade_card);
106107
EXTERN void emu_set_huc6260_color_table_reset_value(int value);
107108
EXTERN void emu_set_huc6280_registers_reset_value(int value);

platforms/shared/desktop/gui.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ bool gui_init(void)
123123
emu_set_scanline_start_end(
124124
config_debug.debug ? 0 : config_video.scanline_start,
125125
config_debug.debug ? 241 : config_video.scanline_end);
126+
emu_set_lowpass_filter(config_video.lowpass_filter, config_video.lowpass_intensity, config_video.lowpass_cutoff_mhz,
127+
config_video.lowpass_speed[0], config_video.lowpass_speed[1], config_video.lowpass_speed[2]);
126128
emu_set_memory_reset_values(
127129
get_reset_value(config_debug.reset_mpr),
128130
get_reset_value(config_debug.reset_ram),

platforms/shared/desktop/gui_menus.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,48 @@ static void menu_video(void)
809809
ImGui::EndMenu();
810810
}
811811

812+
if (ImGui::BeginMenu("Low Pass Filter"))
813+
{
814+
if (ImGui::MenuItem("Enable Low Pass Filter", "", &config_video.lowpass_filter))
815+
{
816+
emu_set_lowpass_filter(config_video.lowpass_filter, config_video.lowpass_intensity, config_video.lowpass_cutoff_mhz,
817+
config_video.lowpass_speed[0], config_video.lowpass_speed[1], config_video.lowpass_speed[2]);
818+
}
819+
ImGui::Separator();
820+
ImGui::PushItemWidth(180.0f);
821+
if (ImGui::SliderFloat("##lpf_intensity", &config_video.lowpass_intensity, 0.0f, 1.0f, "Intensity = %.2f"))
822+
{
823+
emu_set_lowpass_filter(config_video.lowpass_filter, config_video.lowpass_intensity, config_video.lowpass_cutoff_mhz,
824+
config_video.lowpass_speed[0], config_video.lowpass_speed[1], config_video.lowpass_speed[2]);
825+
}
826+
ImGui::PushItemWidth(180.0f);
827+
if (ImGui::SliderFloat("##lpf_cutoff", &config_video.lowpass_cutoff_mhz, 3.0f, 7.0f, "Cutoff = %.1f MHz"))
828+
{
829+
emu_set_lowpass_filter(config_video.lowpass_filter, config_video.lowpass_intensity, config_video.lowpass_cutoff_mhz,
830+
config_video.lowpass_speed[0], config_video.lowpass_speed[1], config_video.lowpass_speed[2]);
831+
}
832+
833+
ImGui::Separator();
834+
ImGui::Text("Apply to speeds:");
835+
if (ImGui::Checkbox("5.36 MHz (256px)", &config_video.lowpass_speed[0]))
836+
{
837+
emu_set_lowpass_filter(config_video.lowpass_filter, config_video.lowpass_intensity, config_video.lowpass_cutoff_mhz,
838+
config_video.lowpass_speed[0], config_video.lowpass_speed[1], config_video.lowpass_speed[2]);
839+
}
840+
if (ImGui::Checkbox("7.16 MHz (341px)", &config_video.lowpass_speed[1]))
841+
{
842+
emu_set_lowpass_filter(config_video.lowpass_filter, config_video.lowpass_intensity, config_video.lowpass_cutoff_mhz,
843+
config_video.lowpass_speed[0], config_video.lowpass_speed[1], config_video.lowpass_speed[2]);
844+
}
845+
if (ImGui::Checkbox("10.8 MHz (512px)", &config_video.lowpass_speed[2]))
846+
{
847+
emu_set_lowpass_filter(config_video.lowpass_filter, config_video.lowpass_intensity, config_video.lowpass_cutoff_mhz,
848+
config_video.lowpass_speed[0], config_video.lowpass_speed[1], config_video.lowpass_speed[2]);
849+
}
850+
851+
ImGui::EndMenu();
852+
}
853+
812854
ImGui::Separator();
813855

814856
if (ImGui::BeginMenu("Background Color"))

src/huc6260.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ HuC6260::HuC6260(HuC6202* huc6202, HuC6280* huc6280)
3939
m_palette = 0;
4040
m_speed = HuC6260_SPEED_5_36_MHZ;
4141
InitPointer(m_frame_buffer);
42+
m_lowpass_enabled = false;
43+
m_lowpass_intensity = 1.0f;
44+
m_lowpass_cutoff_mhz = 5.0f;
45+
m_lowpass_speed[0] = false;
46+
m_lowpass_speed[1] = true;
47+
m_lowpass_speed[2] = true;
4248

4349
CalculateScreenBounds();
4450
}
@@ -316,6 +322,101 @@ void HuC6260::SetCustomPalette(const u8* data)
316322
}
317323
}
318324

325+
void HuC6260::SetLowPassFilter(bool enabled, float intensity, float cutoff_mhz, bool speed_5_36, bool speed_7_16, bool speed_10_8)
326+
{
327+
m_lowpass_enabled = enabled;
328+
m_lowpass_intensity = intensity;
329+
m_lowpass_cutoff_mhz = cutoff_mhz;
330+
m_lowpass_speed[0] = speed_5_36;
331+
m_lowpass_speed[1] = speed_7_16;
332+
m_lowpass_speed[2] = speed_10_8;
333+
}
334+
335+
template <int bytes_per_pixel>
336+
void HuC6260::ApplyLowPassFilter()
337+
{
338+
static const float k_speed_mhz[4] = { 5.36f, 7.16f, 10.8f, 10.8f };
339+
340+
int speed_index = MIN(m_speed, 2);
341+
if (!m_lowpass_speed[speed_index])
342+
return;
343+
344+
float dot_clock = k_speed_mhz[m_speed];
345+
float base_alpha = m_lowpass_cutoff_mhz / dot_clock;
346+
base_alpha = CLAMP(base_alpha, 0.3f, 1.0f);
347+
float alpha = base_alpha + (1.0f - base_alpha) * (1.0f - m_lowpass_intensity);
348+
349+
if (alpha >= 1.0f)
350+
return;
351+
352+
int width = m_scaled_width ? k_huc6260_scaling_width[m_overscan] : k_huc6260_line_width[m_overscan][m_speed];
353+
int height = GetCurrentHeight();
354+
355+
for (int y = 0; y < height; y++)
356+
{
357+
int line_offset = y * width * bytes_per_pixel;
358+
float r_prev, g_prev, b_prev;
359+
360+
if (bytes_per_pixel == 2)
361+
{
362+
u16 first_pixel = *reinterpret_cast<u16*>(m_frame_buffer + line_offset);
363+
r_prev = ((first_pixel >> 11) & 0x1F) * 255.0f / 31.0f;
364+
g_prev = ((first_pixel >> 5) & 0x3F) * 255.0f / 63.0f;
365+
b_prev = (first_pixel & 0x1F) * 255.0f / 31.0f;
366+
}
367+
else
368+
{
369+
r_prev = m_frame_buffer[line_offset + 0];
370+
g_prev = m_frame_buffer[line_offset + 1];
371+
b_prev = m_frame_buffer[line_offset + 2];
372+
}
373+
374+
for (int x = 0; x < width; x++)
375+
{
376+
int idx = line_offset + x * bytes_per_pixel;
377+
float r, g, b;
378+
379+
if (bytes_per_pixel == 2)
380+
{
381+
u16 pixel = *reinterpret_cast<u16*>(m_frame_buffer + idx);
382+
r = ((pixel >> 11) & 0x1F) * 255.0f / 31.0f;
383+
g = ((pixel >> 5) & 0x3F) * 255.0f / 63.0f;
384+
b = (pixel & 0x1F) * 255.0f / 31.0f;
385+
}
386+
else
387+
{
388+
r = m_frame_buffer[idx + 0];
389+
g = m_frame_buffer[idx + 1];
390+
b = m_frame_buffer[idx + 2];
391+
}
392+
393+
r = alpha * r + (1.0f - alpha) * r_prev;
394+
g = alpha * g + (1.0f - alpha) * g_prev;
395+
b = alpha * b + (1.0f - alpha) * b_prev;
396+
397+
if (bytes_per_pixel == 2)
398+
{
399+
u8 r8 = (u8)r;
400+
u8 g8 = (u8)g;
401+
u8 b8 = (u8)b;
402+
u16 pixel = ((r8 * 31 / 255) << 11) | ((g8 * 63 / 255) << 5) | (b8 * 31 / 255);
403+
*reinterpret_cast<u16*>(m_frame_buffer + idx) = pixel;
404+
}
405+
else
406+
{
407+
m_frame_buffer[idx + 0] = (u8)r;
408+
m_frame_buffer[idx + 1] = (u8)g;
409+
m_frame_buffer[idx + 2] = (u8)b;
410+
}
411+
412+
r_prev = r; g_prev = g; b_prev = b;
413+
}
414+
}
415+
}
416+
417+
template void HuC6260::ApplyLowPassFilter<2>();
418+
template void HuC6260::ApplyLowPassFilter<4>();
419+
319420
void HuC6260::SaveState(std::ostream& stream)
320421
{
321422
using namespace std;

src/huc6260.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class HuC6260
8080
void SetResetValue(int value);
8181
void SetPalette(int palette);
8282
void SetCustomPalette(const u8* data);
83+
void SetLowPassFilter(bool enabled, float intensity, float cutoff_mhz, bool speed_5_36, bool speed_7_16, bool speed_10_8);
8384
void SaveState(std::ostream& stream);
8485
void LoadState(std::istream& stream);
8586

@@ -91,6 +92,8 @@ class HuC6260
9192
template <bool SGX, int BPP>
9293
void RenderFrameTemplate();
9394
void CalculateScreenBounds();
95+
template <int BPP>
96+
void ApplyLowPassFilter();
9497

9598
private:
9699
HuC6202* m_huc6202;
@@ -129,6 +132,10 @@ class HuC6260
129132
int m_screen_end_x;
130133
int m_screen_start_y;
131134
int m_screen_end_y;
135+
bool m_lowpass_enabled;
136+
float m_lowpass_intensity;
137+
float m_lowpass_cutoff_mhz;
138+
bool m_lowpass_speed[3];
132139
};
133140

134141
static const HuC6260::HuC6260_Speed k_huc6260_speed[4] = {

src/huc6260_inline.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,6 @@ INLINE bool HuC6260::Clock(u32 cycles)
118118
m_vsync = true;
119119
m_pixel_index = 0;
120120
frame_ready = true;
121-
m_scaled_width = m_multiple_speeds;
122-
if (m_multiple_speeds)
123-
{
124-
m_multiple_speeds = false;
125-
AdjustForMultipleDividers();
126-
}
127121
}
128122

129123
if(m_vpos >= 14 && m_vpos < 256)
@@ -160,6 +154,21 @@ INLINE void HuC6260::RenderFrame()
160154
else
161155
RenderFrameTemplate<false, 4>();
162156
}
157+
158+
m_scaled_width = m_multiple_speeds;
159+
if (m_multiple_speeds)
160+
{
161+
m_multiple_speeds = false;
162+
AdjustForMultipleDividers();
163+
}
164+
165+
if (m_lowpass_enabled)
166+
{
167+
if (m_pixel_format == GG_PIXEL_RGB565)
168+
ApplyLowPassFilter<2>();
169+
else
170+
ApplyLowPassFilter<4>();
171+
}
163172
}
164173

165174
template <bool is_sgx, int bytes_per_pixel>

0 commit comments

Comments
 (0)