Commit c2f09bb4 authored by Jamie Madill's avatar Jamie Madill Committed by Commit Bot

Revert "Add an interface to communicate between the fake openvr and tests"

This reverts commit edb91802.

Reason for revert: Suspecting flaky crashes:
Win10 FYI Release (Intel HD 630) vr_browser_test

https://ci.chromium.org/p/chromium/builders/luci.chromium.ci/Win10%20FYI%20Release%20%28Intel%20HD%20630%29/974

Received fatal exception EXCEPTION_ACCESS_VIOLATION
Backtrace:
	(No symbol) [0x6A3B41A0]
	(No symbol) [0x6A3B174E]
	device::OpenVRRenderLoop::SubmitFrameWithTextureHandle [0x052D8C2F+323]
	device::mojom::VRPresentationProviderStubDispatch::Accept [0x0399C195+1089]
	device::mojom::VRPresentationProviderStub<mojo::RawPtrImplRefTraits<device::mojom::VRPresentationProvider> >::Accept [0x052D9963+19]
	mojo::InterfaceEndpointClient::HandleValidatedMessage [0x03562159+541]
	mojo::FilterChain::Accept [0x0441F4F3+131]
	mojo::InterfaceEndpointClient::HandleIncomingMessage [0x03563006+106]
	mojo::internal::MultiplexRouter::ProcessIncomingMessage [0x0356658A+698]
	mojo::internal::MultiplexRouter::Accept [0x03566111+295]
	mojo::FilterChain::Accept [0x0441F4F3+131]
	mojo::Connector::ReadSingleMessage [0x03560830+364]
	mojo::Connector::ReadAllAvailableMessages [0x03560F0D+87]
<snip>

Bug: 853790

Original change's description:
> Add an interface to communicate between the fake openvr and tests
> 
> This change creates a way for tests to control the behavior of
> the fake OpenVR, and allows devices to send information up to
> tests.
> 
> BUG=801034
> 
> Change-Id: Ie0fe035ada9553114c054717f52788d375d5c62f
> Reviewed-on: https://chromium-review.googlesource.com/1100157
> Commit-Queue: Bill Orr <billorr@chromium.org>
> Reviewed-by: Brian Sheedy <bsheedy@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#567725}

TBR=jam@chromium.org,bsheedy@chromium.org,billorr@chromium.org

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: 801034
Change-Id: Ie55238c46b1d9c34d92bee2b6324539912b47846
Reviewed-on: https://chromium-review.googlesource.com/1104837Reviewed-by: default avatarJamie Madill <jmadill@chromium.org>
Reviewed-by: default avatarBill Orr <billorr@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568093}
parent bfad34f4
include_rules = [
"+mojo/edk/embedder",
"+device/vr/openvr/test/test_hook.h",
"+device/vr/openvr/openvr_device_provider.h"
]
\ No newline at end of file
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/vr/test/mock_openvr_device_hook_base.h"
MockOpenVRBase::MockOpenVRBase() {
device::OpenVRDeviceProvider::SetTestHook(this);
}
MockOpenVRBase::~MockOpenVRBase() {
device::OpenVRDeviceProvider::SetTestHook(nullptr);
}
void MockOpenVRBase::OnFrameSubmitted(device::SubmittedFrameData frame_data) {}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_VR_TEST_MOCK_OPENVR_DEVICE_HOOK_BASE_H_
#define CHROME_BROWSER_VR_TEST_MOCK_OPENVR_DEVICE_HOOK_BASE_H_
#include "device/vr/openvr/openvr_device_provider.h"
#include "device/vr/openvr/test/test_hook.h"
class MockOpenVRBase : public device::OpenVRTestHook {
public:
MockOpenVRBase();
virtual ~MockOpenVRBase();
// OpenVRTestHook
void OnFrameSubmitted(device::SubmittedFrameData frame_data) override;
};
#endif // CHROME_BROWSER_VR_TEST_MOCK_OPENVR_DEVICE_HOOK_BASE_H_
......@@ -54,7 +54,7 @@ VrXrBrowserTestBase::~VrXrBrowserTestBase() = default;
#endif
void VrXrBrowserTestBase::SetUp() {
// Set the environment variable to use the mock OpenVR client.
// Set the environment variable to use the mock OpenVR client
EXPECT_TRUE(env_->SetVar(kVrOverrideEnvVar, MAKE_ABSOLUTE(kVrOverrideVal)));
EXPECT_TRUE(
env_->SetVar(kVrConfigPathEnvVar, MAKE_ABSOLUTE(kVrConfigPathVal)));
......
......@@ -4,53 +4,32 @@
#include "base/environment.h"
#include "base/files/file.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/vr/test/mock_openvr_device_hook_base.h"
#include "chrome/browser/vr/test/vr_browser_test.h"
#include "chrome/browser/vr/test/vr_xr_browser_test.h"
#include "chrome/browser/vr/test/xr_browser_test.h"
#include "device/vr/openvr/test/fake_openvr_log.h"
#include <memory>
namespace vr {
class MyOpenVRMock : public MockOpenVRBase {
public:
void OnFrameSubmitted(device::SubmittedFrameData frame_data) final;
void WaitForFrame() {
DCHECK(!wait_loop_);
if (num_submitted_frames_ > 0)
return;
wait_loop_ = new base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed);
wait_loop_->Run();
delete wait_loop_;
wait_loop_ = nullptr;
}
device::Color last_submitted_color_ = {};
unsigned int num_submitted_frames_ = 0;
private:
base::RunLoop* wait_loop_ = nullptr;
};
void MyOpenVRMock::OnFrameSubmitted(device::SubmittedFrameData frame_data) {
last_submitted_color_ = frame_data.color;
num_submitted_frames_++;
if (wait_loop_) {
wait_loop_->Quit();
}
}
// Pixel test for WebVR/WebXR - start presentation, submit frames, get data back
// out. Validates that a pixel was rendered with the expected color.
void TestPresentationPixelsImpl(VrXrBrowserTestBase* t, std::string filename) {
MyOpenVRMock my_mock;
// Set up environment variable to tell mock device to save pixel logs.
std::unique_ptr<base::Environment> env = base::Environment::Create();
base::ScopedTempDir temp_dir;
base::FilePath log_path;
{
base::ScopedAllowBlockingForTesting allow_files;
EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
log_path = temp_dir.GetPath().Append(FILE_PATH_LITERAL("VRPixelTest.Log"));
EXPECT_TRUE(log_path.MaybeAsASCII() != "") << "Temp dir is non-ascii";
env->SetVar(GetVrPixelLogEnvVarName(), log_path.MaybeAsASCII());
}
// Load the test page, and enter presentation.
t->LoadUrlAndAwaitInitialization(t->GetHtmlTestFile(filename));
......@@ -65,13 +44,38 @@ void TestPresentationPixelsImpl(VrXrBrowserTestBase* t, std::string filename) {
t->ExecuteStepAndWait("finishTest()", t->GetFirstTabWebContents());
t->EndTest(t->GetFirstTabWebContents());
my_mock.WaitForFrame();
device::Color expected = {0, 0, 255, 255};
EXPECT_EQ(expected.r, my_mock.last_submitted_color_.r);
EXPECT_EQ(expected.g, my_mock.last_submitted_color_.g);
EXPECT_EQ(expected.b, my_mock.last_submitted_color_.b);
EXPECT_EQ(expected.a, my_mock.last_submitted_color_.a);
// Try to open the log file.
{
base::ScopedAllowBlockingForTesting allow_files;
std::unique_ptr<base::File> file;
base::Time start = base::Time::Now();
while (!file) {
file = std::make_unique<base::File>(
log_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_DELETE_ON_CLOSE);
if (!file->IsValid()) {
file = nullptr;
}
if (base::Time::Now() - start > t->kPollTimeoutLong)
break;
}
EXPECT_TRUE(file);
// Now parse the log to validate that we ran correctly.
VRSubmittedFrameEvent event;
int read =
file->ReadAtCurrentPos(reinterpret_cast<char*>(&event), sizeof(event));
EXPECT_EQ(read, static_cast<int>(sizeof(event)));
VRSubmittedFrameEvent::Color expected = {0, 0, 255, 255};
EXPECT_EQ(expected.r, event.color.r);
EXPECT_EQ(expected.g, event.color.g);
EXPECT_EQ(expected.b, event.color.b);
EXPECT_EQ(expected.a, event.color.a);
file = nullptr; // Make sure we destroy this before allow_files.
EXPECT_TRUE(temp_dir.Delete());
}
}
IN_PROC_BROWSER_TEST_F(VrBrowserTestStandard,
......
......@@ -1934,8 +1934,6 @@ test("browser_tests") {
"../browser/ui/views/accessibility/invert_bubble_view_browsertest.cc",
"../browser/ui/views/settings_reset_prompt_dialog_browsertest.cc",
"../browser/ui/views/uninstall_view_browsertest.cc",
"../browser/vr/test/mock_openvr_device_hook_base.cc",
"../browser/vr/test/mock_openvr_device_hook_base.h",
"../browser/vr/test/vr_browser_test.cc",
"../browser/vr/test/vr_browser_test.h",
"../browser/vr/test/vr_xr_browser_test.cc",
......@@ -1950,7 +1948,6 @@ test("browser_tests") {
deps += [
"//chrome:other_version",
"//chrome/app:command_ids",
"//device/vr",
"//third_party/wtl",
"//ui/resources",
]
......
......@@ -100,7 +100,6 @@ if (enable_vr) {
"openvr/openvr_render_loop.h",
"openvr/openvr_type_converters.cc",
"openvr/openvr_type_converters.h",
"openvr/test/test_hook.h",
]
}
......@@ -191,9 +190,7 @@ if (enable_openvr) {
sources = [
"openvr/test/fake_openvr_impl_api.cc",
"openvr/test/test_helper.cc",
"openvr/test/test_helper.h",
"openvr/test/test_hook.h",
"openvr/test/fake_openvr_log.h",
]
libs = [
......
......@@ -8,17 +8,8 @@
#include "device/gamepad/gamepad_data_fetcher_manager.h"
#include "device/vr/openvr/openvr_device.h"
#include "device/vr/openvr/openvr_gamepad_data_fetcher.h"
#include "device/vr/openvr/test/test_hook.h"
#include "third_party/openvr/src/headers/openvr.h"
#include "base/command_line.h"
namespace {
// Use the pattern established in content_switches.h, but don't add a content
// dependency -- device shouldn't have one.
constexpr char kTestType[] = "test-type";
} // namespace
namespace device {
void OpenVRDeviceProvider::RecordRuntimeAvailability() {
......@@ -42,13 +33,7 @@ OpenVRDeviceProvider::~OpenVRDeviceProvider() {
device_ = nullptr;
}
if (test_hook_registration_s) {
DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(kTestType));
test_hook_registration_s->SetTestHook(nullptr);
}
vr::VR_Shutdown();
test_hook_registration_s = nullptr;
}
void OpenVRDeviceProvider::Initialize(
......@@ -62,17 +47,6 @@ void OpenVRDeviceProvider::Initialize(
std::move(initialization_complete).Run();
}
void OpenVRDeviceProvider::SetTestHook(OpenVRTestHook* test_hook) {
DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(kTestType));
test_hook_s = test_hook;
if (test_hook_registration_s) {
test_hook_registration_s->SetTestHook(test_hook_s);
}
}
OpenVRTestHook* OpenVRDeviceProvider::test_hook_s = nullptr;
TestHookRegistration* OpenVRDeviceProvider::test_hook_registration_s = nullptr;
void OpenVRDeviceProvider::CreateDevice() {
if (!vr::VR_IsRuntimeInstalled() || !vr::VR_IsHmdPresent())
return;
......@@ -81,16 +55,6 @@ void OpenVRDeviceProvider::CreateDevice() {
vr::IVRSystem* vr_system =
vr::VR_Init(&init_error, vr::EVRApplicationType::VRApplication_Scene);
if (base::CommandLine::ForCurrentProcess()->HasSwitch(kTestType)) {
// Allow our mock implementation of OpenVR to be controlled by tests.
vr::EVRInitError eError;
test_hook_registration_s = reinterpret_cast<TestHookRegistration*>(
vr::VR_GetGenericInterface(kChromeOpenVRTestHookAPI, &eError));
if (test_hook_registration_s) {
test_hook_registration_s->SetTestHook(test_hook_s);
}
}
if (init_error != vr::VRInitError_None) {
LOG(ERROR) << vr::VR_GetVRInitErrorAsEnglishDescription(init_error);
return;
......
......@@ -15,8 +15,6 @@
namespace device {
class OpenVRDevice;
class OpenVRTestHook;
class TestHookRegistration;
class DEVICE_VR_EXPORT OpenVRDeviceProvider : public VRDeviceProvider {
public:
......@@ -33,15 +31,11 @@ class DEVICE_VR_EXPORT OpenVRDeviceProvider : public VRDeviceProvider {
static void RecordRuntimeAvailability();
static void SetTestHook(OpenVRTestHook*);
private:
void CreateDevice();
std::unique_ptr<OpenVRDevice> device_;
bool initialized_ = false;
static OpenVRTestHook* test_hook_s;
static TestHookRegistration* test_hook_registration_s;
DISALLOW_COPY_AND_ASSIGN(OpenVRDeviceProvider);
};
......
......@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/debugger.h"
#include "base/environment.h"
#include "base/files/file.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "device/vr/openvr/test/test_helper.h"
#include "device/vr/openvr/test/test_hook.h"
#include "device/vr/openvr/test/fake_openvr_log.h"
#include "third_party/openvr/src/headers/openvr.h"
#include "third_party/openvr/src/src/ivrclientcore.h"
......@@ -16,6 +19,94 @@
namespace vr {
class TestVRLogger {
public:
void Start() {
// Look for environment variable saying we should log data.
std::unique_ptr<base::Environment> env = base::Environment::Create();
std::string log_filename;
if (env->GetVar(GetVrPixelLogEnvVarName(), &log_filename)) {
base::ScopedAllowBlockingForTesting allow_files;
log_file_ = std::make_unique<base::File>(
base::FilePath::FromUTF8Unsafe(log_filename),
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE);
logging_ = log_file_->IsValid();
}
}
void Stop() {
base::ScopedAllowBlockingForTesting allow_files;
if (logging_) {
log_file_->Flush();
log_file_->Close();
log_file_ = nullptr;
}
logging_ = false;
}
void OnPresentedFrame(ID3D11Texture2D* texture) {
if (!logging_)
return;
VRSubmittedFrameEvent frame;
Microsoft::WRL::ComPtr<ID3D11Device> device;
texture->GetDevice(&device);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
device->GetImmediateContext(&context);
// We copy the submitted texture to a new texture, so we can map it, and
// read back pixel data.
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture_copy;
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
desc.Width = 1;
desc.Height = 1;
desc.MiscFlags = 0;
desc.BindFlags = 0;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
HRESULT hr = device->CreateTexture2D(&desc, nullptr, &texture_copy);
if (FAILED(hr)) {
// We'll have invalid data in log (no frame data) so test will fail.
Stop();
return;
}
D3D11_BOX box = {0, 0, 0, 1, 1, 1}; // a 1-pixel box
context->CopySubresourceRegion(texture_copy.Get(), 0, 0, 0, 0, texture, 0,
&box);
D3D11_MAPPED_SUBRESOURCE map_data = {};
hr = context->Map(texture_copy.Get(), 0, D3D11_MAP_READ, 0, &map_data);
if (FAILED(hr)) {
// We'll have invalid data in log (no frame data) so test will fail.
Stop();
return;
}
VRSubmittedFrameEvent::Color* data =
reinterpret_cast<VRSubmittedFrameEvent::Color*>(map_data.pData);
frame.color = data[0]; // Save top-left pixel value.
context->Unmap(texture_copy.Get(), 0);
WriteVRSubmittedFrameEvent(&frame);
Stop(); // For now only validate one pixel from first frame.
}
private:
void WriteVRSubmittedFrameEvent(VRSubmittedFrameEvent* frame) {
base::ScopedAllowBlockingForTesting allow_files;
log_file_->WriteAtCurrentPos(reinterpret_cast<char*>(frame),
sizeof(*frame));
}
bool logging_ = false;
std::unique_ptr<base::File> log_file_;
};
class TestVRSystem : public IVRSystem {
public:
void GetRecommendedRenderTargetSize(uint32_t* pnWidth,
......@@ -397,16 +488,18 @@ class TestVRClientCore : public IVRClientCore {
}
};
TestHelper g_test_helper;
TestVRLogger g_logger;
TestVRSystem g_system;
TestVRCompositor g_compositor;
TestVRClientCore g_loader;
EVRInitError TestVRClientCore::Init(EVRApplicationType eApplicationType) {
g_logger.Start();
return VRInitError_None;
}
void TestVRClientCore::Cleanup() {
g_logger.Stop();
}
EVRInitError TestVRClientCore::IsInterfaceVersionValid(
......@@ -418,11 +511,9 @@ void* TestVRClientCore::GetGenericInterface(const char* pchNameAndVersion,
EVRInitError* peError) {
*peError = VRInitError_None;
if (strcmp(pchNameAndVersion, IVRSystem_Version) == 0)
return static_cast<IVRSystem*>(&g_system);
return (IVRSystem*)(&g_system);
if (strcmp(pchNameAndVersion, IVRCompositor_Version) == 0)
return static_cast<IVRCompositor*>(&g_compositor);
if (strcmp(pchNameAndVersion, device::kChromeOpenVRTestHookAPI) == 0)
return static_cast<device::TestHookRegistration*>(&g_test_helper);
return (IVRCompositor*)(&g_compositor);
*peError = VRInitError_Init_InvalidInterface;
return nullptr;
......@@ -575,7 +666,7 @@ EVRCompositorError TestVRCompositor::Submit(EVREye,
Texture_t const* texture,
VRTextureBounds_t const*,
EVRSubmitFlags) {
g_test_helper.OnPresentedFrame(
g_logger.OnPresentedFrame(
reinterpret_cast<ID3D11Texture2D*>(texture->handle));
return VRCompositorError_None;
}
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_VR_OPENVR_TEST_FAKE_OPENVR_LOG_H_
#define DEVICE_VR_OPENVR_TEST_FAKE_OPENVR_LOG_H_
namespace vr {
// We log instances of this structure when a frame is submitted.
// Tests can walk the log to validate things ran as expected.
struct VRSubmittedFrameEvent {
struct Color {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
} color;
};
inline char* GetVrPixelLogEnvVarName() {
static char ret[] = "VR_MOCK_LOG_PATH";
return ret;
}
} // namespace vr
#endif // DEVICE_VR_OPENVR_TEST_FAKE_OPENVR_LOG_H_
\ No newline at end of file
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/openvr/test/test_helper.h"
#include "base/debug/debugger.h"
#include "base/logging.h"
#include "base/threading/thread_restrictions.h"
#include "device/vr/openvr/test/test_hook.h"
#include "third_party/openvr/src/headers/openvr.h"
#include "third_party/openvr/src/src/ivrclientcore.h"
#include <D3D11_1.h>
#include <DXGI1_4.h>
#include <wrl.h>
#include <memory>
namespace vr {
void TestHelper::TestFailure() {
NOTREACHED();
}
void TestHelper::OnPresentedFrame(ID3D11Texture2D* texture) {
if (!test_hook_) {
return;
}
Microsoft::WRL::ComPtr<ID3D11Device> device;
texture->GetDevice(&device);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
device->GetImmediateContext(&context);
// We copy the submitted texture to a new texture, so we can map it, and
// read back pixel data.
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture_copy;
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
desc.Width = 1;
desc.Height = 1;
desc.MiscFlags = 0;
desc.BindFlags = 0;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
HRESULT hr = device->CreateTexture2D(&desc, nullptr, &texture_copy);
if (FAILED(hr)) {
TestFailure();
return;
}
D3D11_BOX box = {0, 0, 0, 1, 1, 1}; // a 1-pixel box
context->CopySubresourceRegion(texture_copy.Get(), 0, 0, 0, 0, texture, 0,
&box);
D3D11_MAPPED_SUBRESOURCE map_data = {};
hr = context->Map(texture_copy.Get(), 0, D3D11_MAP_READ, 0, &map_data);
if (FAILED(hr)) {
TestFailure();
return;
}
// We have a 1-pixel image. Give it to the test hook.
device::Color* color = reinterpret_cast<device::Color*>(map_data.pData);
test_hook_->OnFrameSubmitted(device::SubmittedFrameData{color[0]});
context->Unmap(texture_copy.Get(), 0);
}
void TestHelper::SetTestHook(device::OpenVRTestHook* hook) {
test_hook_ = hook;
}
} // namespace vr
\ No newline at end of file
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_VR_OPENVR_TEST_TEST_HELPER_H_
#define DEVICE_VR_OPENVR_TEST_TEST_HELPER_H_
#include "device/vr/openvr/test/test_hook.h"
class ID3D11Texture2D;
namespace vr {
class TestHelper : public device::TestHookRegistration {
public:
// Methods called by mock OpenVR APIs.
void OnPresentedFrame(ID3D11Texture2D* texture);
void TestFailure();
// TestHookRegistration
void SetTestHook(device::OpenVRTestHook* hook) final;
private:
device::OpenVRTestHook* test_hook_ = nullptr;
};
} // namespace vr
#endif // DEVICE_VR_OPENVR_TEST_TEST_HELPER_H_
\ No newline at end of file
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_VR_OPENVR_TEST_TEST_HOOK_H_
#define DEVICE_VR_OPENVR_TEST_TEST_HOOK_H_
namespace device {
// Update this string whenever either interface changes.
constexpr char kChromeOpenVRTestHookAPI[] = "ChromeTestHook_1";
struct Color {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
};
struct SubmittedFrameData {
Color color;
};
// Tests may implement this, and register it to control behavior of OpenVR.
class OpenVRTestHook {
public:
virtual void OnFrameSubmitted(SubmittedFrameData frame_data) = 0;
};
class TestHookRegistration {
public:
virtual void SetTestHook(OpenVRTestHook*) = 0;
};
} // namespace device
#endif // DEVICE_VR_OPENVR_TEST_TEST_HOOK_H_
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment