Implement VideoPlayerControl

This commit is contained in:
2025-09-20 23:49:47 +09:00
parent ed1aca510f
commit 0ebc98f2f1
16 changed files with 958 additions and 1193 deletions

View File

@@ -36,7 +36,9 @@
"Bash(dir:*)",
"Bash(set MSBUILD_EXE=\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\")",
"Bash(%MSBUILD_EXE% \"Vav2Player.sln\" /p:Configuration=Debug /p:Platform=x64 /m)",
"Bash(/c/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe Vav2Player.vcxproj //p:Configuration=Debug //p:Platform=x64 //v:minimal)"
"Bash(/c/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe Vav2Player.vcxproj //p:Configuration=Debug //p:Platform=x64 //v:minimal)",
"Bash(python:*)",
"Bash(start:*)"
],
"deny": [],
"ask": []

View File

@@ -117,6 +117,63 @@ D:\Project\video-av1\
- 언어 표준: C++17 이상
- 런타임: Windows App SDK 1.8
## 코딩 규칙 및 가이드라인
### 주석 언어 규칙
**중요**: 모든 소스 코드 파일의 주석은 **영어로 작성**해야 합니다.
#### 적용 범위
- `.h` 헤더 파일의 모든 주석
- `.cpp` 소스 파일의 모든 주석
- `.xaml.h` WinUI 헤더 파일의 모든 주석
- `.xaml.cpp` WinUI 소스 파일의 모든 주석
#### 예시
```cpp
// ❌ 잘못된 예 (한국어 주석)
// 버퍼 크기 확인 및 재할당 최소화
size_t required_size = frame.width * frame.height * 4;
// ✅ 올바른 예 (영어 주석)
// Check buffer size and minimize reallocation
size_t required_size = frame.width * frame.height * 4;
```
#### 이유
1. **국제화 지원**: 영어 주석으로 코드의 국제적 접근성 향상
2. **컴파일러 호환성**: 일부 컴파일러에서 비ASCII 문자로 인한 인코딩 문제 방지
3. **협업 효율성**: 다양한 배경의 개발자들과의 협업 용이성
4. **유지보수성**: 장기적인 코드 유지보수 시 언어 장벽 제거
#### 주의사항
- 기존 한국어 주석 발견 시 즉시 영어로 변환
- 새로운 코드 작성 시 처음부터 영어 주석 사용
- 함수명, 변수명은 기존 명명 규칙 유지 (영어 또는 한국어 혼용 가능)
### XAML 파일 작성 규칙
**중요**: WinUI XAML 파일에서도 모든 주석과 문자열은 **영어로 작성**해야 합니다.
#### 적용 범위
- `.xaml` 파일의 모든 XML 주석 (`<!-- -->`)
- XAML 속성값의 텍스트 문자열 (예: `Text="..."`, `Content="..."`)
- 사용자에게 표시되지 않는 내부 문자열도 영어 권장
#### 예시
```xml
<!-- ❌ 잘못된 예 (한국어) -->
<!-- 메인 비디오 렌더링 영역 -->
<TextBlock Text="비디오 플레이어" />
<!-- ✅ 올바른 예 (영어) -->
<!-- Main video rendering area -->
<TextBlock Text="Video Player" />
```
#### XAML 파싱 주의사항
- **대량 주석 블록 금지**: 긴 주석 처리된 XAML 코드는 파싱 오류를 일으킬 수 있음
- **사용하지 않는 컨트롤**: 주석 처리보다는 완전 제거 권장
- **이벤트 핸들러**: XAML에서 제거된 컨트롤의 이벤트 핸들러는 .h/.cpp에서도 제거
### 라이브러리 링크 설정
```xml
<!-- 추가 포함 디렉터리 -->

File diff suppressed because it is too large Load Diff

View File

@@ -1,92 +1,29 @@
#pragma once
#include "MainWindow.g.h"
#include "src/Output/FileOutput.h"
#include "src/FileIO/WebMFileReader.h"
#include "src/Decoder/VideoDecoderFactory.h"
#include <memory>
#include <string>
#include <vector>
namespace winrt::Vav2Player::implementation
{
struct MainWindow : MainWindowT<MainWindow>
{
MainWindow()
{
// Initialize XAML components and event handlers
InitializeComponent();
InitializeUI();
}
MainWindow();
// Event handlers
void OpenFileButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void TestDecodeButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void PlayButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void PauseButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void StopButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
// Decoder selection event handler
void DecoderSelectionComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e);
// File Output control event handlers
void EnableFileOutputCheckBox_Checked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void EnableFileOutputCheckBox_Unchecked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void OutputFormatComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e);
private:
// Video processing components
std::unique_ptr<WebMFileReader> m_fileReader;
std::unique_ptr<IVideoDecoder> m_decoder;
std::unique_ptr<FileOutput> m_fileOutput;
// Video rendering components
winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap m_renderBitmap{ nullptr };
std::vector<uint8_t> m_bgraBuffer;
// Current file path
std::string m_currentFilePath;
// Decoder selection state
VideoDecoderFactory::DecoderType m_selectedDecoderType = VideoDecoderFactory::DecoderType::AUTO;
// UI state
bool m_isPlaying = false;
bool m_isFileLoaded = false;
bool m_fileOutputEnabled = false;
// Playback state
uint64_t m_currentFrame = 0;
uint64_t m_totalFrames = 0;
double m_frameRate = 30.0;
bool m_playbackInitialized = false;
// Timer for playback
winrt::Microsoft::UI::Dispatching::DispatcherQueueTimer m_playbackTimer{ nullptr };
// Helper methods
void UpdateStatus(const std::string& message);
void UpdateButtons();
void ProcessSingleFrame();
void UpdateProgress();
void InitializePlayback();
void StartPlaybackTimer();
void StopPlaybackTimer();
void InitializeUI();
std::string GetDecoderTypeName(VideoDecoderFactory::DecoderType decoderType) const;
// File Output helper methods
void UpdateFileOutputControls();
void InitializeFileOutput();
void UpdateOutputFolderDisplay();
// Video rendering methods
void InitializeVideoRenderer();
void RenderFrameToScreen(const VideoFrame& frame);
void ConvertYUVToBGRA(const VideoFrame& yuv_frame, uint8_t* bgra_buffer, uint32_t width, uint32_t height);
// File picker helper
winrt::Windows::Foundation::IAsyncAction OpenFileAsync();
// 성능 최적화된 비디오 렌더링 함수들
static void ConvertYUVToBGRA(const VideoFrame& yuv_frame, uint8_t* bgra_buffer, uint32_t width, uint32_t height);
static void RenderFrameToScreen(const VideoFrame& frame, winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap& bitmap, std::vector<uint8_t>& bgra_buffer);
};
}
@@ -95,4 +32,4 @@ namespace winrt::Vav2Player::factory_implementation
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
{
};
}
}

View File

@@ -108,7 +108,7 @@
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\lib\libwebm;$(ProjectDir)..\..\..\lib\dav1d;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>webm-debug.lib;dav1d-debug.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib</AdditionalDependencies>
<AdditionalDependencies>webm-debug.lib;dav1d-debug.lib;d3d12.lib;dxgi.lib;d3dcompiler.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
@@ -117,7 +117,7 @@
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\lib\libwebm;$(ProjectDir)..\..\..\lib\dav1d;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>webm.lib;dav1d.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib</AdditionalDependencies>
<AdditionalDependencies>webm.lib;dav1d.lib;d3d12.lib;dxgi.lib;d3dcompiler.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib</AdditionalDependencies>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
@@ -156,6 +156,8 @@
<ClInclude Include="src\Pipeline\StreamingPipeline.h" />
<ClInclude Include="src\Output\FileOutput.h" />
<ClInclude Include="src\TestMain.h" />
<ClInclude Include="src\Rendering\D3D12VideoRenderer.h" />
<ClInclude Include="src\Rendering\D3D12Helpers.h" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml" />
@@ -190,6 +192,7 @@
<ClCompile Include="src\Output\FileOutput.cpp" />
<ClCompile Include="src\Console\HeadlessDecoder.cpp" />
<ClCompile Include="src\TestMain.cpp" />
<ClCompile Include="src\Rendering\D3D12VideoRenderer.cpp" />
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>

View File

@@ -8,15 +8,15 @@ namespace Vav2Player
};
[default_interface]
runtimeclass VideoPlayerControl : Windows.UI.Xaml.Controls.UserControl
runtimeclass VideoPlayerControl : Microsoft.UI.Xaml.Controls.UserControl
{
VideoPlayerControl();
// Public Properties
String VideoSource{ get; set; };
Boolean ShowControls{ get; set; };
Boolean AutoPlay{ get; set; };
VideoDecoderType DecoderType{ get; set; };
String VideoSource;
Boolean ShowControls;
Boolean AutoPlay;
VideoDecoderType DecoderType;
// Public Methods
void LoadVideo(String filePath);

View File

@@ -11,15 +11,20 @@
Unloaded="UserControl_Unloaded">
<Grid x:Name="RootGrid" Background="Black">
<!-- 메인 비디오 렌더링 영역 -->
<!-- Main video rendering area -->
<Border x:Name="VideoContainer"
Background="Black"
BorderBrush="Gray"
BorderThickness="1">
<!-- SwapChainPanel for D3D12 rendering (future) -->
<Grid x:Name="VideoDisplayArea">
<!-- Current bitmap-based rendering -->
<!-- Hardware D3D12 rendering (Phase 1) -->
<SwapChainPanel x:Name="VideoSwapChainPanel"
Visibility="Collapsed"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<!-- Software CPU rendering (fallback) -->
<Image x:Name="VideoImage"
Stretch="Uniform"
HorizontalAlignment="Center"
@@ -36,7 +41,7 @@
</Grid>
</Border>
<!-- UI 오버레이 -->
<!-- UI overlay -->
<Grid x:Name="OverlayGrid" Background="Transparent">
<!-- 로딩 인디케이터 -->
<ProgressRing x:Name="LoadingRing"
@@ -61,84 +66,9 @@
TextAlignment="Center"/>
</Border>
<!-- 비디오 컨트롤 바 -->
<Grid x:Name="ControlsGrid"
VerticalAlignment="Bottom"
Background="#CC000000"
Height="60"
Padding="12,8"
Visibility="Collapsed">
<!-- Video control bar (disabled for now) -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 재생/일시정지 -->
<Button x:Name="PlayPauseButton"
Grid.Column="0"
Width="32" Height="32"
Background="Transparent"
BorderThickness="0"
Click="PlayPauseButton_Click">
<SymbolIcon x:Name="PlayPauseIcon"
Symbol="Play"
Foreground="White"/>
</Button>
<!-- 현재 시간 -->
<TextBlock x:Name="CurrentTimeText"
Grid.Column="1"
Text="00:00"
Foreground="White"
VerticalAlignment="Center"
Margin="8,0,8,0"
FontSize="12"/>
<!-- 프로그레스 바 -->
<Slider x:Name="ProgressSlider"
Grid.Column="2"
Minimum="0"
Maximum="100"
Value="0"
VerticalAlignment="Center"
Margin="8,0"
ValueChanged="ProgressSlider_ValueChanged"/>
<!-- 총 시간 -->
<TextBlock x:Name="DurationText"
Grid.Column="3"
Text="00:00"
Foreground="White"
VerticalAlignment="Center"
Margin="8,0,8,0"
FontSize="12"/>
<!-- 컨트롤 버튼들 -->
<StackPanel Grid.Column="4"
Orientation="Horizontal"
Spacing="4">
<Button x:Name="StopButton"
Width="32" Height="32"
Background="Transparent"
BorderThickness="0"
Click="StopButton_Click">
<SymbolIcon Symbol="Stop" Foreground="White"/>
</Button>
<Button x:Name="VolumeButton"
Width="32" Height="32"
Background="Transparent"
BorderThickness="0">
<SymbolIcon Symbol="Volume" Foreground="White"/>
</Button>
</StackPanel>
</Grid>
<!-- 마우스 오버시 컨트롤 표시 -->
<!-- Show controls on mouse hover -->
<Border x:Name="HoverDetector"
Background="Transparent"
PointerEntered="HoverDetector_PointerEntered"

View File

@@ -5,6 +5,8 @@
#endif
#include <winrt/Microsoft.UI.Dispatching.h>
#include <algorithm>
#include <cstring>
using namespace winrt;
using namespace winrt::Microsoft::UI::Xaml;
@@ -14,6 +16,7 @@ using namespace winrt::Microsoft::UI::Dispatching;
namespace winrt::Vav2Player::implementation
{
VideoPlayerControl::VideoPlayerControl()
: m_useHardwareRendering(false)
{
InitializeComponent();
}
@@ -29,7 +32,7 @@ namespace winrt::Vav2Player::implementation
// Set initial UI state
if (!m_showControls)
{
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
}
// Auto load video if source is set
@@ -68,6 +71,8 @@ namespace winrt::Vav2Player::implementation
}
}
// Control event handlers (commented out as controls are disabled)
/*
void VideoPlayerControl::PlayPauseButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
{
if (m_isPlaying)
@@ -79,12 +84,16 @@ namespace winrt::Vav2Player::implementation
Play();
}
}
*/
/*
void VideoPlayerControl::StopButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
{
Stop();
}
*/
/*
void VideoPlayerControl::ProgressSlider_ValueChanged(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e)
{
if (!m_isLoaded || !m_fileReader)
@@ -105,6 +114,7 @@ namespace winrt::Vav2Player::implementation
// Ignore seek errors
}
}
*/
void VideoPlayerControl::HoverDetector_PointerEntered(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const&)
{
@@ -153,11 +163,11 @@ namespace winrt::Vav2Player::implementation
{
if (value && m_isLoaded)
{
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
}
else
{
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
}
}
}
@@ -209,6 +219,35 @@ namespace winrt::Vav2Player::implementation
SetInternalDecoderType(newType);
}
bool VideoPlayerControl::UseHardwareRendering()
{
return m_useHardwareRendering;
}
void VideoPlayerControl::UseHardwareRendering(bool value)
{
if (m_useHardwareRendering != value)
{
m_useHardwareRendering = value;
// Switch rendering method
if (value)
{
// Enable D3D12 hardware rendering (to be implemented in Phase 2)
VideoSwapChainPanel().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
VideoImage().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
OutputDebugStringA("Switched to hardware D3D12 rendering\n");
}
else
{
// Switch to CPU software rendering
VideoSwapChainPanel().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
VideoImage().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
OutputDebugStringA("Switched to software CPU rendering\n");
}
}
}
VideoDecoderFactory::DecoderType VideoPlayerControl::GetInternalDecoderType()
{
return m_decoderType;
@@ -295,11 +334,11 @@ namespace winrt::Vav2Player::implementation
if (m_showControls)
{
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
}
// Update duration display
DurationText().Text(winrt::to_hstring(FormatTime(m_duration)));
// DurationText().Text(winrt::to_hstring(FormatTime(m_duration)));
UpdateStatus(L"Video loaded successfully");
OutputDebugStringA(("Video loaded: " + filePathStr + "\n").c_str());
@@ -553,26 +592,60 @@ namespace winrt::Vav2Player::implementation
if (!frame.is_valid || !m_renderBitmap)
return;
// Declare variables at function scope to avoid compiler issues
size_t required_size = 0;
uint8_t* dest_pixels = nullptr;
try
{
// Convert YUV to BGRA
// Check buffer size and minimize reallocation
required_size = frame.width * frame.height * 4;
if (m_bgraBuffer.size() != required_size) {
m_bgraBuffer.resize(required_size);
}
// Convert YUV to BGRA (optimized version)
ConvertYUVToBGRA(frame, m_bgraBuffer.data(), frame.width, frame.height);
// Update bitmap
// Direct access to WriteableBitmap pixel buffer (improved caching)
auto buffer = m_renderBitmap.PixelBuffer();
auto byteAccess = buffer.as<::IBufferByteAccess>();
uint8_t* pixels = nullptr;
byteAccess->Buffer(&pixels);
byteAccess->Buffer(&dest_pixels);
if (pixels)
if (dest_pixels)
{
memcpy(pixels, m_bgraBuffer.data(), m_bgraBuffer.size());
// Large memory copy optimization (using 64-bit alignment)
if (required_size >= 64 && (reinterpret_cast<uintptr_t>(dest_pixels) % 8) == 0 &&
(reinterpret_cast<uintptr_t>(m_bgraBuffer.data()) % 8) == 0)
{
// Copy in 8-byte units (faster memory transfer)
uint64_t* src64 = reinterpret_cast<uint64_t*>(m_bgraBuffer.data());
uint64_t* dst64 = reinterpret_cast<uint64_t*>(dest_pixels);
size_t qwords = required_size / 8;
size_t remainder = required_size % 8;
for (size_t i = 0; i < qwords; i++) {
dst64[i] = src64[i];
}
// Copy remaining bytes
if (remainder > 0) {
memcpy(dest_pixels + qwords * 8, m_bgraBuffer.data() + qwords * 8, remainder);
}
}
else
{
// Standard memory copy
memcpy(dest_pixels, m_bgraBuffer.data(), required_size);
}
// Invalidate bitmap (trigger UI update)
m_renderBitmap.Invalidate();
}
}
catch (...)
{
// Ignore rendering errors
// Ignore rendering errors (logging removed for performance)
}
}
@@ -586,27 +659,68 @@ namespace winrt::Vav2Player::implementation
const int32_t u_stride = yuv_frame.u_stride;
const int32_t v_stride = yuv_frame.v_stride;
for (uint32_t row = 0; row < height; ++row)
{
for (uint32_t col = 0; col < width; ++col)
{
int32_t y = y_plane[row * y_stride + col];
int32_t u = u_plane[(row / 2) * u_stride + (col / 2)];
int32_t v = v_plane[(row / 2) * v_stride + (col / 2)];
// Declare variables at function scope to avoid compiler issues
int r_offset = 0, g_u_offset = 0, g_v_offset = 0, b_offset = 0;
uint32_t pixel_offset = 0;
int c = y - 16;
int d = u - 128;
int e = v - 128;
// Optimization using lookup tables (initialized once with static variables)
static bool lookup_initialized = false;
static int16_t r_v_table[256];
static int16_t g_u_table[256];
static int16_t g_v_table[256];
static int16_t b_u_table[256];
int r = std::clamp((298 * c + 409 * e + 128) >> 8, 0, 255);
int g = std::clamp((298 * c - 100 * d - 208 * e + 128) >> 8, 0, 255);
int b = std::clamp((298 * c + 516 * d + 128) >> 8, 0, 255);
if (!lookup_initialized) {
for (int i = 0; i < 256; i++) {
int uv_val = i - 128;
r_v_table[i] = (409 * uv_val + 128) >> 8;
g_u_table[i] = (100 * uv_val + 128) >> 8;
g_v_table[i] = (208 * uv_val + 128) >> 8;
b_u_table[i] = (516 * uv_val + 128) >> 8;
}
lookup_initialized = true;
}
uint32_t pixel_offset = (row * width + col) * 4;
bgra_buffer[pixel_offset + 0] = static_cast<uint8_t>(b);
bgra_buffer[pixel_offset + 1] = static_cast<uint8_t>(g);
bgra_buffer[pixel_offset + 2] = static_cast<uint8_t>(r);
bgra_buffer[pixel_offset + 3] = 255;
// Process in 2x2 blocks to reuse UV values (improved cache efficiency)
for (uint32_t row = 0; row < height; row += 2) {
for (uint32_t col = 0; col < width; col += 2) {
// One UV value per 2x2 block
uint32_t uv_row = row / 2;
uint32_t uv_col = col / 2;
if (uv_row >= height / 2 || uv_col >= width / 2) continue;
int u = u_plane[uv_row * u_stride + uv_col];
int v = v_plane[uv_row * v_stride + uv_col];
// Get conversion coefficients from lookup tables
r_offset = r_v_table[v];
g_u_offset = g_u_table[u];
g_v_offset = g_v_table[v];
b_offset = b_u_table[u];
// Process 4 pixels in 2x2 block
for (int dy = 0; dy < 2 && (row + dy) < height; dy++) {
for (int dx = 0; dx < 2 && (col + dx) < width; dx++) {
uint32_t pixel_row = row + dy;
uint32_t pixel_col = col + dx;
int y = y_plane[pixel_row * y_stride + pixel_col];
int y_scaled = (298 * (y - 16) + 128) >> 8;
// RGB calculation (using lookup table values)
int r = std::clamp(y_scaled + r_offset, 0, 255);
int g = std::clamp(y_scaled - g_u_offset - g_v_offset, 0, 255);
int b = std::clamp(y_scaled + b_offset, 0, 255);
// Store in BGRA format
pixel_offset = (pixel_row * width + pixel_col) * 4;
bgra_buffer[pixel_offset + 0] = static_cast<uint8_t>(b);
bgra_buffer[pixel_offset + 1] = static_cast<uint8_t>(g);
bgra_buffer[pixel_offset + 2] = static_cast<uint8_t>(r);
bgra_buffer[pixel_offset + 3] = 255;
}
}
}
}
}
@@ -635,15 +749,15 @@ namespace winrt::Vav2Player::implementation
// Update play/pause button
if (m_isPlaying)
{
PlayPauseIcon().Symbol(winrt::Microsoft::UI::Xaml::Controls::Symbol::Pause);
// PlayPauseIcon().Symbol(winrt::Microsoft::UI::Xaml::Controls::Symbol::Pause);
}
else
{
PlayPauseIcon().Symbol(winrt::Microsoft::UI::Xaml::Controls::Symbol::Play);
// PlayPauseIcon().Symbol(winrt::Microsoft::UI::Xaml::Controls::Symbol::Play);
}
// Update current time display
CurrentTimeText().Text(winrt::to_hstring(FormatTime(m_currentTime)));
// CurrentTimeText().Text(winrt::to_hstring(FormatTime(m_currentTime)));
}
void VideoPlayerControl::UpdateProgress()
@@ -651,7 +765,7 @@ namespace winrt::Vav2Player::implementation
if (m_duration > 0)
{
double progress = (m_currentTime / m_duration) * 100.0;
ProgressSlider().Value(progress);
// ProgressSlider().Value(progress);
}
}
@@ -691,7 +805,7 @@ namespace winrt::Vav2Player::implementation
{
if (m_showControls && m_isLoaded)
{
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
}
}
@@ -699,7 +813,7 @@ namespace winrt::Vav2Player::implementation
{
if (m_showControls && m_isPlaying)
{
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
}
}
@@ -760,7 +874,7 @@ namespace winrt::Vav2Player::implementation
m_renderBitmap = nullptr;
VideoImage().Source(nullptr);
PlaceholderText().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
// ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
}
bool VideoPlayerControl::CreateDecoder()
@@ -796,4 +910,4 @@ namespace winrt::Vav2Player::implementation
return false;
}
}
}
}

View File

@@ -4,6 +4,7 @@
#include "src/FileIO/WebMFileReader.h"
#include "src/Decoder/VideoDecoderFactory.h"
#include "src/Common/VideoTypes.h"
#include "src/Rendering/D3D12VideoRenderer.h"
#include <memory>
#include <string>
#include <atomic>
@@ -17,9 +18,10 @@ namespace winrt::Vav2Player::implementation
// Events
void UserControl_Loaded(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void UserControl_Unloaded(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void PlayPauseButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void StopButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void ProgressSlider_ValueChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e);
// Control event handlers (commented out as controls are disabled)
// void PlayPauseButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
// void StopButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
// void ProgressSlider_ValueChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e);
void HoverDetector_PointerEntered(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& e);
void HoverDetector_PointerExited(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& e);
@@ -36,6 +38,10 @@ namespace winrt::Vav2Player::implementation
Vav2Player::VideoDecoderType DecoderType();
void DecoderType(Vav2Player::VideoDecoderType value);
// Hardware rendering control
bool UseHardwareRendering();
void UseHardwareRendering(bool value);
// Internal decoder type management
VideoDecoderFactory::DecoderType GetInternalDecoderType();
void SetInternalDecoderType(VideoDecoderFactory::DecoderType value);
@@ -63,6 +69,10 @@ namespace winrt::Vav2Player::implementation
winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap m_renderBitmap{ nullptr };
std::vector<uint8_t> m_bgraBuffer;
// D3D12 Hardware rendering (Phase 1)
std::unique_ptr<D3D12VideoRenderer> m_d3d12Renderer;
bool m_useHardwareRendering;
// Configuration
winrt::hstring m_videoSource;
bool m_showControls = true;

View File

@@ -42,6 +42,13 @@
#include <robuffer.h>
#include <wrl.h>
// D3D12 DirectX Graphics
#include <d3d12.h>
#include <dxgi1_6.h>
#include <d3dcompiler.h>
#include <directxmath.h>
#include <wrl/client.h>
// Forward declare IBufferByteAccess
#ifndef __IBufferByteAccess_INTERFACE_DEFINED__
#define __IBufferByteAccess_INTERFACE_DEFINED__

View File

@@ -1,4 +1,4 @@
#include "pch.h"
#include "pch.h"
#include "FileOutput.h"
#include "../Common/PermissionUtils.h"
#include <iostream>
@@ -32,7 +32,7 @@ FileOutput::FileOutput(const OutputConfig& config)
OutputDebugStringA("[FileOutput] YUV->RGB conversion will use lookup table optimization\n");
}
// 권한 체크 및 설정
// Check and set permissions
if (!CheckAndSetupPermissions()) {
OutputDebugStringA("[FileOutput] Warning: Failed to setup proper permissions for output directory\n");
}
@@ -777,7 +777,7 @@ bool FileOutput::CheckAndSetupPermissions() {
OutputDebugStringA(("[FileOutput] Checking permissions for output directory: " + full_output_path.string() + "\n").c_str());
// 권한 체크 및 처리
// Check and handle permissions
auto permission_result = PermissionUtils::CheckAndHandlePermissions(full_output_path);
switch (permission_result) {
@@ -805,33 +805,33 @@ bool FileOutput::CheckAndSetupPermissions() {
bool FileOutput::HandlePermissionDenied() {
try {
// 안전한 출력 디렉토리 찾기
// Find safe output directory
std::filesystem::path safe_directory = PermissionUtils::GetSafeOutputDirectory();
OutputDebugStringA(("[FileOutput] Using safe output directory: " + safe_directory.string() + "\n").c_str());
// 설정 업데이트
// Update settings
m_config.output_directory = safe_directory;
// 캐시 재초기화
// Reinitialize cache
InitializeCache();
// 권한 재체크
// Recheck permissions
auto permission_result = PermissionUtils::CheckDirectoryCreatePermission(safe_directory);
if (permission_result == PermissionUtils::PermissionResult::Granted) {
OutputDebugStringA("[FileOutput] Safe directory permissions verified\n");
// 사용자에게 알림
// Notify user
std::wstring safe_dir_wstr = std::wstring(safe_directory.string().begin(), safe_directory.string().end());
std::wstring message = L"원래 출력 디렉토리에 권한이 없어 다음 위치를 사용합니다:\n" + safe_dir_wstr;
PermissionUtils::ShowPermissionDialog(message, L"출력 디렉토리 변경");
std::wstring message = L"Permission denied for original output directory. Using alternate location:\n" + safe_dir_wstr;
PermissionUtils::ShowPermissionDialog(message, L"Output Directory Changed");
return true;
} else {
OutputDebugStringA("[FileOutput] Safe directory also lacks permissions\n");
// 마지막 수단: 임시 디렉토리
// Last resort: temporary directory
wchar_t temp_path[MAX_PATH];
if (GetTempPathW(MAX_PATH, temp_path)) {
std::filesystem::path temp_dir = std::filesystem::path(temp_path) / "Vav2Player_Emergency";
@@ -841,8 +841,8 @@ bool FileOutput::HandlePermissionDenied() {
OutputDebugStringA(("[FileOutput] Using emergency temp directory: " + temp_dir.string() + "\n").c_str());
std::wstring temp_dir_wstr = std::wstring(temp_dir.string().begin(), temp_dir.string().end());
std::wstring message = L"안전한 출력 디렉토리를 찾을 수 없어 임시 폴더를 사용합니다:\n" + temp_dir_wstr;
PermissionUtils::ShowPermissionDialog(message, L"긴급 출력 디렉토리");
std::wstring message = L"Cannot find safe output directory. Using temporary folder:\n" + temp_dir_wstr;
PermissionUtils::ShowPermissionDialog(message, L"Emergency Output Directory");
return true;
} else {

View File

@@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "../Common/VideoTypes.h"
#include "../Common/PermissionUtils.h"
#include <string>

View File

@@ -0,0 +1,60 @@
#pragma once
#include <d3d12.h>
#include <d3dx12.h>
namespace Vav2Player {
// D3D12 utility functions and helpers
class D3D12Helpers
{
public:
// Error checking
static HRESULT ThrowIfFailed(HRESULT hr);
static const char* GetD3D12ErrorString(HRESULT hr);
// Resource creation helpers
static HRESULT CreateBuffer(ID3D12Device* device,
UINT64 size,
D3D12_HEAP_TYPE heapType,
D3D12_RESOURCE_STATES initialState,
ID3D12Resource** ppResource);
static HRESULT CreateTexture2D(ID3D12Device* device,
UINT width,
UINT height,
DXGI_FORMAT format,
D3D12_RESOURCE_FLAGS flags,
D3D12_RESOURCE_STATES initialState,
ID3D12Resource** ppResource);
// Upload data helpers
static HRESULT UploadTextureData(ID3D12Device* device,
ID3D12GraphicsCommandList* commandList,
ID3D12Resource* destTexture,
const void* srcData,
UINT srcRowPitch,
UINT srcSlicePitch);
private:
D3D12Helpers() = delete; // Static class only
};
// RAII wrapper for D3D12 command list recording
class ScopedCommandList
{
public:
ScopedCommandList(ID3D12GraphicsCommandList* commandList,
ID3D12CommandAllocator* allocator,
ID3D12PipelineState* pipelineState = nullptr);
~ScopedCommandList();
ID3D12GraphicsCommandList* Get() const { return m_commandList; }
operator ID3D12GraphicsCommandList*() const { return m_commandList; }
private:
ID3D12GraphicsCommandList* m_commandList;
bool m_needsClose;
};
} // namespace Vav2Player

View File

@@ -0,0 +1,376 @@
#include "pch.h"
#include "D3D12VideoRenderer.h"
#include "d3dx12.h"
#include <windows.h>
#include <winrt/Microsoft.UI.Xaml.Hosting.h>
#include <microsoft.ui.xaml.media.dxinterop.h>
namespace Vav2Player {
D3D12VideoRenderer::D3D12VideoRenderer()
: m_isInitialized(false)
, m_width(0)
, m_height(0)
, m_frameIndex(0)
, m_rtvDescriptorSize(0)
, m_fenceEvent(nullptr)
{
// Initialize fence values
for (UINT i = 0; i < FrameCount; i++)
{
m_fenceValues[i] = 0;
}
}
D3D12VideoRenderer::~D3D12VideoRenderer()
{
Shutdown();
}
HRESULT D3D12VideoRenderer::Initialize(winrt::Microsoft::UI::Xaml::Controls::SwapChainPanel const& panel,
uint32_t width, uint32_t height)
{
if (m_isInitialized)
{
return S_OK;
}
m_width = width;
m_height = height;
HRESULT hr = S_OK;
// 1. Create D3D12 device
hr = CreateDevice();
if (FAILED(hr)) return hr;
// 2. Create command queue
hr = CreateCommandQueue();
if (FAILED(hr)) return hr;
// 3. Create swap chain
hr = CreateSwapChain(panel);
if (FAILED(hr)) return hr;
// 4. Create descriptor heaps
hr = CreateDescriptorHeaps();
if (FAILED(hr)) return hr;
// 5. Create render targets
hr = CreateRenderTargets();
if (FAILED(hr)) return hr;
// 6. Create fence and event
hr = CreateFenceAndEvent();
if (FAILED(hr)) return hr;
m_isInitialized = true;
return S_OK;
}
void D3D12VideoRenderer::Shutdown()
{
if (!m_isInitialized)
return;
// Wait for GPU to finish
if (m_commandQueue)
{
WaitForPreviousFrame();
}
// Close fence event
if (m_fenceEvent)
{
CloseHandle(m_fenceEvent);
m_fenceEvent = nullptr;
}
// Reset all ComPtr objects (automatic cleanup)
m_device.Reset();
m_commandQueue.Reset();
m_swapChain.Reset();
m_rtvHeap.Reset();
m_commandAllocator.Reset();
m_commandList.Reset();
m_fence.Reset();
for (UINT i = 0; i < FrameCount; i++)
{
m_renderTargets[i].Reset();
m_fenceValues[i] = 0;
}
m_isInitialized = false;
}
HRESULT D3D12VideoRenderer::RenderSolidColor(float r, float g, float b, float a)
{
if (!m_isInitialized)
return E_FAIL;
HRESULT hr = S_OK;
// Wait for previous frame
hr = WaitForPreviousFrame();
if (FAILED(hr)) return hr;
// Reset command allocator
hr = m_commandAllocator->Reset();
if (FAILED(hr)) return hr;
// Reset command list
hr = m_commandList->Reset(m_commandAllocator.Get(), nullptr);
if (FAILED(hr)) return hr;
// Get current render target
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(
m_rtvHeap->GetCPUDescriptorHandleForHeapStart(),
m_frameIndex,
m_rtvDescriptorSize);
// Set render target
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
// Clear render target with solid color
const float clearColor[] = { r, g, b, a };
m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
// Transition render target to present state
CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::CreateTransition(
m_renderTargets[m_frameIndex].Get(),
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PRESENT);
m_commandList->ResourceBarrier(1, &barrier);
// Close command list
hr = m_commandList->Close();
if (FAILED(hr)) return hr;
// Execute command list
ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
// Present
hr = m_swapChain->Present(1, 0);
if (FAILED(hr)) return hr;
// Update frame index
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
return S_OK;
}
HRESULT D3D12VideoRenderer::RenderFrame(const VideoFrame& frame)
{
// TODO: Phase 3에서 YUV 텍스처 렌더링 구현
// 현재는 단색 렌더링으로 대체
return RenderSolidColor(0.0f, 0.5f, 1.0f, 1.0f); // 파란색
}
HRESULT D3D12VideoRenderer::Resize(uint32_t width, uint32_t height)
{
if (!m_isInitialized)
return E_FAIL;
if (m_width == width && m_height == height)
return S_OK;
// Wait for GPU
WaitForPreviousFrame();
// Release render targets
for (UINT i = 0; i < FrameCount; i++)
{
m_renderTargets[i].Reset();
}
// Resize swap chain
HRESULT hr = m_swapChain->ResizeBuffers(FrameCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0);
if (FAILED(hr)) return hr;
m_width = width;
m_height = height;
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
// Recreate render targets
return CreateRenderTargets();
}
// Private helper methods implementation
HRESULT D3D12VideoRenderer::CreateDevice()
{
// Enable debug layer in debug builds
#ifdef _DEBUG
ComPtr<ID3D12Debug> debugController;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
{
debugController->EnableDebugLayer();
}
#endif
// Create DXGI factory
ComPtr<IDXGIFactory4> factory;
HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));
if (FAILED(hr)) return hr;
// Try to create hardware device
ComPtr<IDXGIAdapter1> hardwareAdapter;
for (UINT adapterIndex = 0;
DXGI_ERROR_NOT_FOUND != factory->EnumAdapters1(adapterIndex, &hardwareAdapter);
++adapterIndex)
{
DXGI_ADAPTER_DESC1 desc;
hardwareAdapter->GetDesc1(&desc);
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
continue;
// Try to create device
if (SUCCEEDED(D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device))))
break;
}
if (!m_device)
{
// Fallback to WARP device
ComPtr<IDXGIAdapter> warpAdapter;
hr = factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter));
if (FAILED(hr)) return hr;
hr = D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
if (FAILED(hr)) return hr;
}
return S_OK;
}
HRESULT D3D12VideoRenderer::CreateCommandQueue()
{
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
HRESULT hr = m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue));
if (FAILED(hr)) return hr;
// Create command allocator
hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator));
if (FAILED(hr)) return hr;
// Create command list
hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
m_commandAllocator.Get(), nullptr, IID_PPV_ARGS(&m_commandList));
if (FAILED(hr)) return hr;
// Close command list (will be reset before use)
hr = m_commandList->Close();
return hr;
}
HRESULT D3D12VideoRenderer::CreateSwapChain(winrt::Microsoft::UI::Xaml::Controls::SwapChainPanel const& panel)
{
// Get DXGI factory
ComPtr<IDXGIFactory4> factory;
HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));
if (FAILED(hr)) return hr;
// Describe swap chain
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.BufferCount = FrameCount;
swapChainDesc.Width = m_width;
swapChainDesc.Height = m_height;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.SampleDesc.Count = 1;
ComPtr<IDXGISwapChain1> swapChain;
hr = factory->CreateSwapChainForComposition(m_commandQueue.Get(), &swapChainDesc, nullptr, &swapChain);
if (FAILED(hr)) return hr;
hr = swapChain.As(&m_swapChain);
if (FAILED(hr)) return hr;
// Associate swap chain with SwapChainPanel
auto panelNative = panel.as<ISwapChainPanelNative>();
hr = panelNative->SetSwapChain(m_swapChain.Get());
if (FAILED(hr)) return hr;
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
return S_OK;
}
HRESULT D3D12VideoRenderer::CreateDescriptorHeaps()
{
// Create RTV descriptor heap
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = FrameCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
HRESULT hr = m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap));
if (FAILED(hr)) return hr;
m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
return S_OK;
}
HRESULT D3D12VideoRenderer::CreateRenderTargets()
{
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());
// Create RTV for each frame
for (UINT n = 0; n < FrameCount; n++)
{
HRESULT hr = m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n]));
if (FAILED(hr)) return hr;
m_device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle);
rtvHandle.Offset(1, m_rtvDescriptorSize);
}
return S_OK;
}
HRESULT D3D12VideoRenderer::CreateFenceAndEvent()
{
HRESULT hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
if (FAILED(hr)) return hr;
m_fenceValues[m_frameIndex] = 1;
// Create event for fence
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (m_fenceEvent == nullptr)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
HRESULT D3D12VideoRenderer::WaitForPreviousFrame()
{
// Signal and wait for fence
const UINT64 fence = m_fenceValues[m_frameIndex];
HRESULT hr = m_commandQueue->Signal(m_fence.Get(), fence);
if (FAILED(hr)) return hr;
m_fenceValues[m_frameIndex]++;
// Wait for fence to complete
if (m_fence->GetCompletedValue() < fence)
{
hr = m_fence->SetEventOnCompletion(fence, m_fenceEvent);
if (FAILED(hr)) return hr;
WaitForSingleObject(m_fenceEvent, INFINITE);
}
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
return S_OK;
}
} // namespace Vav2Player

View File

@@ -0,0 +1,75 @@
#pragma once
#include <d3d12.h>
#include <dxgi1_6.h>
#include <wrl/client.h>
#include <memory>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include "../Common/VideoTypes.h"
using Microsoft::WRL::ComPtr;
namespace Vav2Player {
class D3D12VideoRenderer
{
public:
D3D12VideoRenderer();
~D3D12VideoRenderer();
// 초기화 및 해제
HRESULT Initialize(winrt::Microsoft::UI::Xaml::Controls::SwapChainPanel const& panel,
uint32_t width, uint32_t height);
void Shutdown();
// 렌더링
HRESULT RenderFrame(const VideoFrame& frame);
HRESULT RenderSolidColor(float r, float g, float b, float a = 1.0f);
// 상태 확인
bool IsInitialized() const { return m_isInitialized; }
uint32_t GetWidth() const { return m_width; }
uint32_t GetHeight() const { return m_height; }
// 리사이즈
HRESULT Resize(uint32_t width, uint32_t height);
private:
// D3D12 Core Objects
ComPtr<ID3D12Device> m_device;
ComPtr<ID3D12CommandQueue> m_commandQueue;
ComPtr<IDXGISwapChain3> m_swapChain;
ComPtr<ID3D12DescriptorHeap> m_rtvHeap;
// Command Objects
ComPtr<ID3D12CommandAllocator> m_commandAllocator;
ComPtr<ID3D12GraphicsCommandList> m_commandList;
// Render Targets
static const UINT FrameCount = 2;
ComPtr<ID3D12Resource> m_renderTargets[FrameCount];
UINT m_frameIndex;
// Synchronization
ComPtr<ID3D12Fence> m_fence;
UINT64 m_fenceValues[FrameCount];
HANDLE m_fenceEvent;
// State
bool m_isInitialized;
uint32_t m_width;
uint32_t m_height;
UINT m_rtvDescriptorSize;
// Helper methods
HRESULT CreateDevice();
HRESULT CreateCommandQueue();
HRESULT CreateSwapChain(winrt::Microsoft::UI::Xaml::Controls::SwapChainPanel const& panel);
HRESULT CreateDescriptorHeaps();
HRESULT CreateRenderTargets();
HRESULT CreateFenceAndEvent();
HRESULT WaitForPreviousFrame();
HRESULT PopulateCommandList();
};
} // namespace Vav2Player

View File

@@ -0,0 +1,62 @@
#pragma once
// Minimal D3D12 helper classes
// Full version available at: https://github.com/Microsoft/DirectX-Graphics-Samples
#include <d3d12.h>
struct CD3DX12_RESOURCE_BARRIER : public D3D12_RESOURCE_BARRIER
{
CD3DX12_RESOURCE_BARRIER() = default;
explicit CD3DX12_RESOURCE_BARRIER(const D3D12_RESOURCE_BARRIER& o) : D3D12_RESOURCE_BARRIER(o) {}
static inline CD3DX12_RESOURCE_BARRIER CreateTransition(
ID3D12Resource* pResource,
D3D12_RESOURCE_STATES stateBefore,
D3D12_RESOURCE_STATES stateAfter,
UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
{
CD3DX12_RESOURCE_BARRIER result = {};
result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
result.Flags = flags;
result.Transition.pResource = pResource;
result.Transition.StateBefore = stateBefore;
result.Transition.StateAfter = stateAfter;
result.Transition.Subresource = subresource;
return result;
}
};
struct CD3DX12_CPU_DESCRIPTOR_HANDLE : public D3D12_CPU_DESCRIPTOR_HANDLE
{
CD3DX12_CPU_DESCRIPTOR_HANDLE() = default;
explicit CD3DX12_CPU_DESCRIPTOR_HANDLE(const D3D12_CPU_DESCRIPTOR_HANDLE& o) : D3D12_CPU_DESCRIPTOR_HANDLE(o) {}
CD3DX12_CPU_DESCRIPTOR_HANDLE(D3D12_CPU_DESCRIPTOR_HANDLE other, INT offsetScaledByIncrementSize)
{
ptr = other.ptr + offsetScaledByIncrementSize;
}
CD3DX12_CPU_DESCRIPTOR_HANDLE(D3D12_CPU_DESCRIPTOR_HANDLE other, INT offsetInDescriptors, UINT descriptorIncrementSize)
{
ptr = other.ptr + offsetInDescriptors * descriptorIncrementSize;
}
CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetInDescriptors, UINT descriptorIncrementSize)
{
ptr += offsetInDescriptors * descriptorIncrementSize;
return *this;
}
CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetScaledByIncrementSize)
{
ptr += offsetScaledByIncrementSize;
return *this;
}
bool operator==(const D3D12_CPU_DESCRIPTOR_HANDLE& other) const
{
return ptr == other.ptr;
}
bool operator!=(const D3D12_CPU_DESCRIPTOR_HANDLE& other) const
{
return ptr != other.ptr;
}
};