From 2c5fedae4b71698918b97111903023772f354b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 20 Jul 2009 15:18:05 +0000 Subject: [PATCH] Applied patch by Gerald Zajac, revised by me to match our coding style: * imported X.org's GTF function, and use it to compute modes that are not on the list. * Also, accept a 6% variation for the selected mode refresh rate. * Automatic whitespace cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31654 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/preferences/screen/Jamfile | 1 + src/preferences/screen/ScreenMode.cpp | 77 ++++++++++------- src/preferences/screen/gtf.cpp | 116 ++++++++++++++++++++++++++ src/preferences/screen/gtf.h | 18 ++++ 4 files changed, 180 insertions(+), 32 deletions(-) create mode 100644 src/preferences/screen/gtf.cpp create mode 100644 src/preferences/screen/gtf.h diff --git a/src/preferences/screen/Jamfile b/src/preferences/screen/Jamfile index 5258a8dfb3..bd6c6c3093 100644 --- a/src/preferences/screen/Jamfile +++ b/src/preferences/screen/Jamfile @@ -9,6 +9,7 @@ UsePrivateHeaders [ FDirName graphics radeon ] ; Preference Screen : AlertView.cpp AlertWindow.cpp + gtf.cpp MonitorView.cpp multimon.cpp RefreshSlider.cpp diff --git a/src/preferences/screen/ScreenMode.cpp b/src/preferences/screen/ScreenMode.cpp index 99cfcf8df2..6461586794 100644 --- a/src/preferences/screen/ScreenMode.cpp +++ b/src/preferences/screen/ScreenMode.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2005-2008, Haiku. + * Copyright 2005-2009, Haiku. * Distributed under the terms of the MIT License. * * Authors: @@ -8,6 +8,7 @@ #include "ScreenMode.h" +#include "gtf.h" #include @@ -49,8 +50,8 @@ get_refresh_rate(display_mode& mode) { // we have to be catious as refresh rate cannot be controlled directly, // so it suffers under rounding errors and hardware restrictions - return rint(10 * float(mode.timing.pixel_clock * 1000) / - float(mode.timing.h_total * mode.timing.v_total)) / 10.0; + return rint(10 * float(mode.timing.pixel_clock * 1000) + / float(mode.timing.h_total * mode.timing.v_total)) / 10.0; } @@ -189,10 +190,10 @@ ScreenMode::Set(const screen_mode& mode, int32 workspace) UpdateOriginalModes(); BScreen screen(fWindow); - + if (workspace == ~0) workspace = current_workspace(); - + // TODO: our app_server doesn't fully support workspaces, yet SetSwapDisplays(&screen, mode.swap_displays); SetUseLaptopPanel(&screen, mode.use_laptop_panel); @@ -211,15 +212,15 @@ ScreenMode::Get(screen_mode& mode, int32 workspace) const { display_mode displayMode; BScreen screen(fWindow); - + if (workspace == ~0) workspace = current_workspace(); - + if (screen.GetMode(workspace, &displayMode) != B_OK) return B_ERROR; - + mode.SetTo(displayMode); - + // TODO: our app_server doesn't fully support workspaces, yet if (GetSwapDisplays(&screen, &mode.swap_displays) != B_OK) mode.swap_displays = false; @@ -239,9 +240,9 @@ ScreenMode::GetOriginalMode(screen_mode& mode, int32 workspace) const workspace = current_workspace(); else if(workspace > 31) return B_BAD_INDEX; - + mode = fOriginal[workspace]; - + return B_OK; } @@ -275,7 +276,7 @@ ScreenMode::Revert() if (result != B_OK) break; } - + return result; } @@ -350,10 +351,10 @@ ScreenMode::GetDisplayMode(const screen_mode& mode, display_mode& displayMode) int32 bestIndex = -1; float bestDiff = 999; - virtualWidth = mode.combine == kCombineHorizontally ? - mode.width * 2 : mode.width; - virtualHeight = mode.combine == kCombineVertically ? - mode.height * 2 : mode.height; + virtualWidth = mode.combine == kCombineHorizontally + ? mode.width * 2 : mode.width; + virtualHeight = mode.combine == kCombineVertically + ? mode.height * 2 : mode.height; // try to find mode in list provided by driver for (uint32 i = 0; i < fModeCount; i++) { @@ -362,18 +363,34 @@ ScreenMode::GetDisplayMode(const screen_mode& mode, display_mode& displayMode) || (color_space)fModeList[i].space != mode.space) continue; - float refresh = get_refresh_rate(fModeList[i]); - if (refresh == mode.refresh) { - // we have luck - we can use this mode directly + // Accept the mode if the computed refresh rate of the mode is within + // 0.6 percent of the refresh rate specified by the caller. Note that + // refresh rates computed from mode parameters is not exact; especially + // some of the older modes such as 640x480, 800x600, and 1024x768. + // The tolerance of 0.6% was obtained by examining the various possible + // modes. + + float refreshDiff = fabs(get_refresh_rate(fModeList[i]) - mode.refresh); + if (refreshDiff < 0.006 * mode.refresh) { + // Accept this mode. displayMode = fModeList[i]; displayMode.h_display_start = 0; displayMode.v_display_start = 0; + + // Since the computed refresh rate of the selected mode might differ + // from selected refresh rate by a few tenths (e.g. 60.2 instead of + // 60.0), tweak the pixel clock so the the refresh rate of the mode + // matches the selected refresh rate. + + displayMode.timing.pixel_clock = uint32(((displayMode.timing.h_total + * displayMode.timing.v_total * mode.refresh) / 1000.0) + 0.5); return true; } - float diff = fabs(refresh - mode.refresh); - if (diff < bestDiff) { - bestDiff = diff; + // Mode not acceptable. + + if (refreshDiff < bestDiff) { + bestDiff = refreshDiff; bestIndex = i; } } @@ -382,20 +399,16 @@ ScreenMode::GetDisplayMode(const screen_mode& mode, display_mode& displayMode) if (bestIndex == -1) return false; - // now, we are better of using GMT formula, but - // as we don't have it, we just tune the pixel - // clock of the best mode. - displayMode = fModeList[bestIndex]; displayMode.h_display_start = 0; displayMode.v_display_start = 0; - // after some fiddling, it looks like this is the formula - // used by the original panel (notice that / 10 happens before - // multiplying with refresh rate - this leads to different - // rounding) - displayMode.timing.pixel_clock = ((uint32)displayMode.timing.h_total - * displayMode.timing.v_total / 10 * int32(mode.refresh * 10)) / 1000; + // For the mode selected by the width, height, and refresh rate, compute + // the video timing parameters for the mode by using the VESA Generalized + // Timing Formula (GTF). + + ComputeGTFVideoTiming(displayMode.timing.h_display, + displayMode.timing.v_display, mode.refresh, displayMode.timing); return true; } diff --git a/src/preferences/screen/gtf.cpp b/src/preferences/screen/gtf.cpp new file mode 100644 index 0000000000..0fa367d77f --- /dev/null +++ b/src/preferences/screen/gtf.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2009 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Authors: + * Gerald Zajac + */ + +/*! This file contains function(s) to generate video mode timings using the + GTF Timing Standard. The code in this file was adapted from the X.org + source file gtf.c which has the following copyright: + + Copyright (c) 2001, Andy Ritger aritger@nvidia.com All rights reserved. +*/ + + +#include "gtf.h" + +#include + + +#define CELL_GRAN 8.0 // assumed character cell granularity +#define MIN_PORCH 1 // minimum front porch +#define V_SYNC_RQD 3 // width of vsync in lines +#define H_SYNC_PERCENT 8.0 // width of hsync as % of total line +#define MIN_VSYNC_PLUS_BP 550.0 // min time of vsync + back porch (microsec) + +// C' and M' are part of the Blanking Duty Cycle computation. + +#define C_PRIME 30.0 +#define M_PRIME 300.0 + + +/*! Computes the timing values for the specified video mode using the + VESA Generalized Timing Formula (GTF). + \a width is the display width in pixels, \a lines is the display height + in lines, and \a refreshRate is the refresh rate in Hz. The computed + timing values are returned in \a modeTiming. +*/ +void +ComputeGTFVideoTiming(int width, int lines, double refreshRate, + display_timing& modeTiming) +{ + // In order to give correct results, the number of horizontal pixels + // requested is first processed to ensure that it is divisible by the + // character size, by rounding it to the nearest character cell boundary. + + width = int((width / CELL_GRAN) * CELL_GRAN); + + // Estimate the Horizontal period. + + double horizontalPeriodEstimate = (((1.0 / refreshRate) + - (MIN_VSYNC_PLUS_BP / 1000000.0)) / (lines + MIN_PORCH) * 1000000.0); + + // Compute the number of lines in V sync + back porch. + + double verticalSyncAndBackPorch + = rint(MIN_VSYNC_PLUS_BP / horizontalPeriodEstimate); + + // Compute the total number of lines in Vertical field period. + + double totalLines = lines + verticalSyncAndBackPorch + MIN_PORCH; + + // Estimate the Vertical field frequency. + + double verticalFieldRateEstimate = 1.0 / horizontalPeriodEstimate + / totalLines * 1000000.0; + + // Compute the actual horizontal period. + + double horizontalPeriod = horizontalPeriodEstimate + / (refreshRate / verticalFieldRateEstimate); + + // Compute the ideal blanking duty cycle from the blanking duty cycle + // equation. + + double idealDutyCycle = C_PRIME - (M_PRIME * horizontalPeriod / 1000.0); + + // Compute the number of pixels in the horizontal blanking time to the + // nearest double character cell. + + double horizontalBlank = rint(width * idealDutyCycle + / (100.0 - idealDutyCycle) + / (2.0 * CELL_GRAN)) * (2.0 * CELL_GRAN); + + // Compute the total number of pixels in a horizontal line. + + double totalWidth = width + horizontalBlank; + + // Compute the number of pixels in the horizontal sync period. + + double horizontalSync + = rint(H_SYNC_PERCENT / 100.0 * totalWidth / CELL_GRAN) * CELL_GRAN; + + // Compute the number of pixels in the horizontal front porch period. + + double horizontalFrontPorch = (horizontalBlank / 2.0) - horizontalSync; + + // Finally, return the results in a display_timing struct. + + modeTiming.pixel_clock = uint32(totalWidth * 1000.0 / horizontalPeriod); + + modeTiming.h_display = uint16(width); + modeTiming.h_sync_start = uint16(width + horizontalFrontPorch); + modeTiming.h_sync_end + = uint16(width + horizontalFrontPorch + horizontalSync); + modeTiming.h_total = uint16(totalWidth); + + modeTiming.v_display = uint16(lines); + modeTiming.v_sync_start = uint16(lines + MIN_PORCH); + modeTiming.v_sync_end = uint16(lines + MIN_PORCH + V_SYNC_RQD); + modeTiming.v_total = uint16(totalLines); + + modeTiming.flags = B_POSITIVE_VSYNC; + // GTF timings use -hSync and +vSync +} diff --git a/src/preferences/screen/gtf.h b/src/preferences/screen/gtf.h new file mode 100644 index 0000000000..73a8d17ec1 --- /dev/null +++ b/src/preferences/screen/gtf.h @@ -0,0 +1,18 @@ +/* + * Copyright 2009 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Authors: + * Gerald Zajac + */ +#ifndef GTF_H +#define GTF_H + + +#include + + +void ComputeGTFVideoTiming(int width, int lines, double refreshRate, + display_timing& modeTiming); + +#endif // GTF_H