Skip to content

Commit

Permalink
Merge pull request #446 from jammm/pbrt-hip-upstream-pr1
Browse files Browse the repository at this point in the history
Add PBRT_CPU_GPU in some signatures;Move OptiX
  • Loading branch information
mmp authored Oct 7, 2024
2 parents 867721d + 5a28e28 commit 5374fba
Show file tree
Hide file tree
Showing 44 changed files with 333 additions and 335 deletions.
12 changes: 6 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -694,17 +694,17 @@ SET (PBRT_UTIL_SOURCE_HEADERS

if (PBRT_CUDA_ENABLED)
set (PBRT_GPU_SOURCE
src/pbrt/gpu/aggregate.cpp
src/pbrt/gpu/denoiser.cpp
src/pbrt/gpu/optix/aggregate.cpp
src/pbrt/gpu/optix/denoiser.cpp
src/pbrt/gpu/memory.cpp
src/pbrt/gpu/util.cpp
)
set (PBRT_GPU_SOURCE_HEADERS
src/pbrt/gpu/aggregate.h
src/pbrt/gpu/optix/aggregate.h
src/pbrt/gpu/cudagl.h
src/pbrt/gpu/denoiser.h
src/pbrt/gpu/optix/denoiser.h
src/pbrt/gpu/memory.h
src/pbrt/gpu/optix.h
src/pbrt/gpu/optix/optix.h
src/pbrt/gpu/util.h
)

Expand Down Expand Up @@ -770,7 +770,7 @@ if (PBRT_CUDA_ENABLED)
PROPERTIES LANGUAGE CUDA
)

cuda_compile_and_embed (PBRT_EMBEDDED_PTX src/pbrt/gpu/optix.cu optix.cu)
cuda_compile_and_embed (PBRT_EMBEDDED_PTX src/pbrt/gpu/optix/optix.cu optix.cu)
endif ()

source_group ("Source Files" FILES ${PBRT_SOURCE})
Expand Down
2 changes: 1 addition & 1 deletion src/pbrt/bssrdf.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ PBRT_CPU_GPU inline void SubsurfaceFromDiffuse(const BSSRDFTable &t,
}
}

inline pstd::optional<BSSRDFProbeSegment> BSSRDF::SampleSp(Float u1, Point2f u2) const {
PBRT_CPU_GPU inline pstd::optional<BSSRDFProbeSegment> BSSRDF::SampleSp(Float u1, Point2f u2) const {
auto sample = [&](auto ptr) { return ptr->SampleSp(u1, u2); };
return Dispatch(sample);
}
Expand Down
29 changes: 15 additions & 14 deletions src/pbrt/bxdfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ std::string LayeredBxDF<TopBxDF, BottomBxDF, twoSided>::ToString() const {
}

// DielectricBxDF Method Definitions
pstd::optional<BSDFSample> DielectricBxDF::Sample_f(
PBRT_CPU_GPU pstd::optional<BSDFSample> DielectricBxDF::Sample_f(
Vector3f wo, Float uc, Point2f u, TransportMode mode,
BxDFReflTransFlags sampleFlags) const {
if (eta == 1 || mfDistrib.EffectivelySmooth()) {
Expand Down Expand Up @@ -169,7 +169,7 @@ pstd::optional<BSDFSample> DielectricBxDF::Sample_f(
}
}

SampledSpectrum DielectricBxDF::f(Vector3f wo, Vector3f wi, TransportMode mode) const {
PBRT_CPU_GPU SampledSpectrum DielectricBxDF::f(Vector3f wo, Vector3f wi, TransportMode mode) const {
if (eta == 1 || mfDistrib.EffectivelySmooth())
return SampledSpectrum(0.f);
// Evaluate rough dielectric BSDF
Expand Down Expand Up @@ -208,7 +208,7 @@ SampledSpectrum DielectricBxDF::f(Vector3f wo, Vector3f wi, TransportMode mode)
}
}

Float DielectricBxDF::PDF(Vector3f wo, Vector3f wi, TransportMode mode,
PBRT_CPU_GPU Float DielectricBxDF::PDF(Vector3f wo, Vector3f wi, TransportMode mode,
BxDFReflTransFlags sampleFlags) const {
if (eta == 1 || mfDistrib.EffectivelySmooth())
return 0;
Expand Down Expand Up @@ -272,7 +272,7 @@ std::string ConductorBxDF::ToString() const {
}

// HairBxDF Method Definitions
HairBxDF::HairBxDF(Float h, Float eta, const SampledSpectrum &sigma_a, Float beta_m,
PBRT_CPU_GPU HairBxDF::HairBxDF(Float h, Float eta, const SampledSpectrum &sigma_a, Float beta_m,
Float beta_n, Float alpha)
: h(h), eta(eta), sigma_a(sigma_a), beta_m(beta_m), beta_n(beta_n) {
CHECK(h >= -1 && h <= 1);
Expand Down Expand Up @@ -300,7 +300,7 @@ HairBxDF::HairBxDF(Float h, Float eta, const SampledSpectrum &sigma_a, Float bet
}
}

SampledSpectrum HairBxDF::f(Vector3f wo, Vector3f wi, TransportMode mode) const {
PBRT_CPU_GPU SampledSpectrum HairBxDF::f(Vector3f wo, Vector3f wi, TransportMode mode) const {
// Compute hair coordinate system terms related to _wo_
Float sinTheta_o = wo.x;
Float cosTheta_o = SafeSqrt(1 - Sqr(sinTheta_o));
Expand Down Expand Up @@ -365,7 +365,7 @@ SampledSpectrum HairBxDF::f(Vector3f wo, Vector3f wi, TransportMode mode) const
return fsum;
}

pstd::array<Float, HairBxDF::pMax + 1> HairBxDF::ApPDF(Float cosTheta_o) const {
PBRT_CPU_GPU pstd::array<Float, HairBxDF::pMax + 1> HairBxDF::ApPDF(Float cosTheta_o) const {
// Initialize array of $A_p$ values for _cosTheta_o_
Float sinTheta_o = SafeSqrt(1 - Sqr(cosTheta_o));
// Compute $\cos\,\thetat$ for refracted ray
Expand Down Expand Up @@ -394,7 +394,7 @@ pstd::array<Float, HairBxDF::pMax + 1> HairBxDF::ApPDF(Float cosTheta_o) const {
return apPDF;
}

pstd::optional<BSDFSample> HairBxDF::Sample_f(Vector3f wo, Float uc, Point2f u,
PBRT_CPU_GPU pstd::optional<BSDFSample> HairBxDF::Sample_f(Vector3f wo, Float uc, Point2f u,
TransportMode mode,
BxDFReflTransFlags sampleFlags) const {
// Compute hair coordinate system terms related to _wo_
Expand Down Expand Up @@ -489,7 +489,7 @@ pstd::optional<BSDFSample> HairBxDF::Sample_f(Vector3f wo, Float uc, Point2f u,
return BSDFSample(f(wo, wi, mode), wi, pdf, Flags());
}

Float HairBxDF::PDF(Vector3f wo, Vector3f wi, TransportMode mode,
PBRT_CPU_GPU Float HairBxDF::PDF(Vector3f wo, Vector3f wi, TransportMode mode,
BxDFReflTransFlags sampleFlags) const {
// TODO? flags...

Expand Down Expand Up @@ -545,7 +545,7 @@ Float HairBxDF::PDF(Vector3f wo, Vector3f wi, TransportMode mode,
return pdf;
}

RGBUnboundedSpectrum HairBxDF::SigmaAFromConcentration(Float ce, Float cp) {
PBRT_CPU_GPU RGBUnboundedSpectrum HairBxDF::SigmaAFromConcentration(Float ce, Float cp) {
RGB eumelaninSigma_a(0.419f, 0.697f, 1.37f);
RGB pheomelaninSigma_a(0.187f, 0.4f, 1.05f);
RGB sigma_a = ce * eumelaninSigma_a + cp * pheomelaninSigma_a;
Expand All @@ -556,7 +556,7 @@ RGBUnboundedSpectrum HairBxDF::SigmaAFromConcentration(Float ce, Float cp) {
#endif
}

SampledSpectrum HairBxDF::SigmaAFromReflectance(const SampledSpectrum &c, Float beta_n,
PBRT_CPU_GPU SampledSpectrum HairBxDF::SigmaAFromReflectance(const SampledSpectrum &c, Float beta_n,
const SampledWavelengths &lambda) {
SampledSpectrum sigma_a;
for (int i = 0; i < NSpectrumSamples; ++i)
Expand Down Expand Up @@ -996,7 +996,8 @@ MeasuredBxDFData *MeasuredBxDF::BRDFDataFromFile(const std::string &filename,
}

// MeasuredBxDF Method Definitions
SampledSpectrum MeasuredBxDF::f(Vector3f wo, Vector3f wi, TransportMode mode) const {
PBRT_CPU_GPU SampledSpectrum MeasuredBxDF::f(Vector3f wo, Vector3f wi,
TransportMode mode) const {
// Check for valid reflection configurations
if (!SameHemisphere(wo, wi))
return SampledSpectrum(0);
Expand Down Expand Up @@ -1032,7 +1033,7 @@ SampledSpectrum MeasuredBxDF::f(Vector3f wo, Vector3f wi, TransportMode mode) co
(4 * brdf->sigma.Evaluate(u_wo) * CosTheta(wi));
}

pstd::optional<BSDFSample> MeasuredBxDF::Sample_f(Vector3f wo, Float uc, Point2f u,
PBRT_CPU_GPU pstd::optional<BSDFSample> MeasuredBxDF::Sample_f(Vector3f wo, Float uc, Point2f u,
TransportMode mode,
BxDFReflTransFlags sampleFlags) const {
// Check flags and detect interactions in lower hemisphere
Expand Down Expand Up @@ -1083,7 +1084,7 @@ pstd::optional<BSDFSample> MeasuredBxDF::Sample_f(Vector3f wo, Float uc, Point2f
return BSDFSample(fr, wi, pdf * lum_pdf, BxDFFlags::GlossyReflection);
}

Float MeasuredBxDF::PDF(Vector3f wo, Vector3f wi, TransportMode mode,
PBRT_CPU_GPU Float MeasuredBxDF::PDF(Vector3f wo, Vector3f wi, TransportMode mode,
BxDFReflTransFlags sampleFlags) const {
if (!(sampleFlags & BxDFReflTransFlags::Reflection))
return 0;
Expand Down Expand Up @@ -1127,7 +1128,7 @@ std::string NormalizedFresnelBxDF::ToString() const {
}

// BxDF Method Definitions
SampledSpectrum BxDF::rho(Vector3f wo, pstd::span<const Float> uc,
PBRT_CPU_GPU SampledSpectrum BxDF::rho(Vector3f wo, pstd::span<const Float> uc,
pstd::span<const Point2f> u2) const {
if (wo.z == 0)
return {};
Expand Down
10 changes: 5 additions & 5 deletions src/pbrt/bxdfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1131,12 +1131,12 @@ class NormalizedFresnelBxDF {
Float eta;
};

inline SampledSpectrum BxDF::f(Vector3f wo, Vector3f wi, TransportMode mode) const {
PBRT_CPU_GPU inline SampledSpectrum BxDF::f(Vector3f wo, Vector3f wi, TransportMode mode) const {
auto f = [&](auto ptr) -> SampledSpectrum { return ptr->f(wo, wi, mode); };
return Dispatch(f);
}

inline pstd::optional<BSDFSample> BxDF::Sample_f(Vector3f wo, Float uc, Point2f u,
PBRT_CPU_GPU inline pstd::optional<BSDFSample> BxDF::Sample_f(Vector3f wo, Float uc, Point2f u,
TransportMode mode,
BxDFReflTransFlags sampleFlags) const {
auto sample_f = [&](auto ptr) -> pstd::optional<BSDFSample> {
Expand All @@ -1145,18 +1145,18 @@ inline pstd::optional<BSDFSample> BxDF::Sample_f(Vector3f wo, Float uc, Point2f
return Dispatch(sample_f);
}

inline Float BxDF::PDF(Vector3f wo, Vector3f wi, TransportMode mode,
PBRT_CPU_GPU inline Float BxDF::PDF(Vector3f wo, Vector3f wi, TransportMode mode,
BxDFReflTransFlags sampleFlags) const {
auto pdf = [&](auto ptr) { return ptr->PDF(wo, wi, mode, sampleFlags); };
return Dispatch(pdf);
}

inline BxDFFlags BxDF::Flags() const {
PBRT_CPU_GPU inline BxDFFlags BxDF::Flags() const {
auto flags = [&](auto ptr) { return ptr->Flags(); };
return Dispatch(flags);
}

inline void BxDF::Regularize() {
PBRT_CPU_GPU inline void BxDF::Regularize() {
auto regularize = [&](auto ptr) { ptr->Regularize(); };
return Dispatch(regularize);
}
Expand Down
34 changes: 17 additions & 17 deletions src/pbrt/cameras.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,24 @@ std::string CameraTransform::ToString() const {
}

// Camera Method Definitions
pstd::optional<CameraRayDifferential> Camera::GenerateRayDifferential(
PBRT_CPU_GPU pstd::optional<CameraRayDifferential> Camera::GenerateRayDifferential(
CameraSample sample, SampledWavelengths &lambda) const {
auto gen = [&](auto ptr) { return ptr->GenerateRayDifferential(sample, lambda); };
return Dispatch(gen);
}

SampledSpectrum Camera::We(const Ray &ray, SampledWavelengths &lambda,
PBRT_CPU_GPU SampledSpectrum Camera::We(const Ray &ray, SampledWavelengths &lambda,
Point2f *pRaster2) const {
auto we = [&](auto ptr) { return ptr->We(ray, lambda, pRaster2); };
return Dispatch(we);
}

void Camera::PDF_We(const Ray &ray, Float *pdfPos, Float *pdfDir) const {
PBRT_CPU_GPU void Camera::PDF_We(const Ray &ray, Float *pdfPos, Float *pdfDir) const {
auto pdf = [&](auto ptr) { return ptr->PDF_We(ray, pdfPos, pdfDir); };
return Dispatch(pdf);
}

pstd::optional<CameraWiSample> Camera::SampleWi(const Interaction &ref, Point2f u,
PBRT_CPU_GPU pstd::optional<CameraWiSample> Camera::SampleWi(const Interaction &ref, Point2f u,
SampledWavelengths &lambda) const {
auto sample = [&](auto ptr) { return ptr->SampleWi(ref, u, lambda); };
return Dispatch(sample);
Expand Down Expand Up @@ -113,7 +113,7 @@ CameraBase::CameraBase(CameraBaseParameters p)
"the system may crash as a result of this.");
}

pstd::optional<CameraRayDifferential> CameraBase::GenerateRayDifferential(
PBRT_CPU_GPU pstd::optional<CameraRayDifferential> CameraBase::GenerateRayDifferential(
Camera camera, CameraSample sample, SampledWavelengths &lambda) {
// Generate regular camera ray _cr_ for ray differential
pstd::optional<CameraRay> cr = camera.GenerateRay(sample, lambda);
Expand Down Expand Up @@ -281,7 +281,7 @@ CameraBaseParameters::CameraBaseParameters(const CameraTransform &cameraTransfor
}

// OrthographicCamera Method Definitions
pstd::optional<CameraRay> OrthographicCamera::GenerateRay(
PBRT_CPU_GPU pstd::optional<CameraRay> OrthographicCamera::GenerateRay(
CameraSample sample, SampledWavelengths &lambda) const {
// Compute raster and camera sample positions
Point3f pFilm = Point3f(sample.pFilm.x, sample.pFilm.y, 0);
Expand All @@ -305,7 +305,7 @@ pstd::optional<CameraRay> OrthographicCamera::GenerateRay(
return CameraRay{RenderFromCamera(ray)};
}

pstd::optional<CameraRayDifferential> OrthographicCamera::GenerateRayDifferential(
PBRT_CPU_GPU pstd::optional<CameraRayDifferential> OrthographicCamera::GenerateRayDifferential(
CameraSample sample, SampledWavelengths &lambda) const {
// Compute main orthographic viewing ray
// Compute raster and camera sample positions
Expand Down Expand Up @@ -401,7 +401,7 @@ OrthographicCamera *OrthographicCamera::Create(const ParameterDictionary &parame
}

// PerspectiveCamera Method Definitions
pstd::optional<CameraRay> PerspectiveCamera::GenerateRay(
PBRT_CPU_GPU pstd::optional<CameraRay> PerspectiveCamera::GenerateRay(
CameraSample sample, SampledWavelengths &lambda) const {
// Compute raster and camera sample positions
Point3f pFilm = Point3f(sample.pFilm.x, sample.pFilm.y, 0);
Expand All @@ -426,7 +426,7 @@ pstd::optional<CameraRay> PerspectiveCamera::GenerateRay(
return CameraRay{RenderFromCamera(ray)};
}

pstd::optional<CameraRayDifferential> PerspectiveCamera::GenerateRayDifferential(
PBRT_CPU_GPU pstd::optional<CameraRayDifferential> PerspectiveCamera::GenerateRayDifferential(
CameraSample sample, SampledWavelengths &lambda) const {
// Compute raster and camera sample positions
Point3f pFilm = Point3f(sample.pFilm.x, sample.pFilm.y, 0);
Expand Down Expand Up @@ -527,7 +527,7 @@ PerspectiveCamera *PerspectiveCamera::Create(const ParameterDictionary &paramete
lensradius, focaldistance);
}

SampledSpectrum PerspectiveCamera::We(const Ray &ray, SampledWavelengths &lambda,
PBRT_CPU_GPU SampledSpectrum PerspectiveCamera::We(const Ray &ray, SampledWavelengths &lambda,
Point2f *pRasterOut) const {
// Check if ray is forward-facing with respect to the camera
Float cosTheta = Dot(ray.d, RenderFromCamera(Vector3f(0, 0, 1), ray.time));
Expand Down Expand Up @@ -555,7 +555,7 @@ SampledSpectrum PerspectiveCamera::We(const Ray &ray, SampledWavelengths &lambda
return SampledSpectrum(1 / (A * lensArea * Pow<4>(cosTheta)));
}

void PerspectiveCamera::PDF_We(const Ray &ray, Float *pdfPos, Float *pdfDir) const {
PBRT_CPU_GPU void PerspectiveCamera::PDF_We(const Ray &ray, Float *pdfPos, Float *pdfDir) const {
// Return zero PDF values if ray direction is not front-facing
Float cosTheta = Dot(ray.d, RenderFromCamera(Vector3f(0, 0, 1), ray.time));
if (cosTheta <= cosTotalWidth) {
Expand All @@ -581,7 +581,7 @@ void PerspectiveCamera::PDF_We(const Ray &ray, Float *pdfPos, Float *pdfDir) con
*pdfDir = 1 / (A * Pow<3>(cosTheta));
}

pstd::optional<CameraWiSample> PerspectiveCamera::SampleWi(
PBRT_CPU_GPU pstd::optional<CameraWiSample> PerspectiveCamera::SampleWi(
const Interaction &ref, Point2f u, SampledWavelengths &lambda) const {
// Uniformly sample a lens interaction _lensIntr_
Point2f pLens = lensRadius * SampleUniformDiskConcentric(u);
Expand All @@ -607,7 +607,7 @@ pstd::optional<CameraWiSample> PerspectiveCamera::SampleWi(
}

// SphericalCamera Method Definitions
pstd::optional<CameraRay> SphericalCamera::GenerateRay(CameraSample sample,
PBRT_CPU_GPU pstd::optional<CameraRay> SphericalCamera::GenerateRay(CameraSample sample,
SampledWavelengths &lambda) const {
// Compute spherical camera ray direction
Point2f uv(sample.pFilm.x / film.FullResolution().x,
Expand Down Expand Up @@ -746,7 +746,7 @@ RealisticCamera::RealisticCamera(CameraBaseParameters baseParameters,
FindMinimumDifferentials(this);
}

Float RealisticCamera::TraceLensesFromFilm(const Ray &rCamera, Ray *rOut) const {
PBRT_CPU_GPU Float RealisticCamera::TraceLensesFromFilm(const Ray &rCamera, Ray *rOut) const {
Float elementZ = 0, weight = 1;
// Transform _rCamera_ from camera to lens system space
Ray rLens(Point3f(rCamera.o.x, rCamera.o.y, -rCamera.o.z),
Expand Down Expand Up @@ -894,7 +894,7 @@ Bounds2f RealisticCamera::BoundExitPupil(Float filmX0, Float filmX1) const {
return pupilBounds;
}

pstd::optional<ExitPupilSample> RealisticCamera::SampleExitPupil(Point2f pFilm,
PBRT_CPU_GPU pstd::optional<ExitPupilSample> RealisticCamera::SampleExitPupil(Point2f pFilm,
Point2f uLens) const {
// Find exit pupil bound for sample distance from film center
Float rFilm = std::sqrt(Sqr(pFilm.x) + Sqr(pFilm.y));
Expand All @@ -916,7 +916,7 @@ pstd::optional<ExitPupilSample> RealisticCamera::SampleExitPupil(Point2f pFilm,
return ExitPupilSample{pPupil, pdf};
}

pstd::optional<CameraRay> RealisticCamera::GenerateRay(CameraSample sample,
PBRT_CPU_GPU pstd::optional<CameraRay> RealisticCamera::GenerateRay(CameraSample sample,
SampledWavelengths &lambda) const {
// Find point on film, _pFilm_, corresponding to _sample.pFilm_
Point2f s(sample.pFilm.x / film.FullResolution().x,
Expand Down Expand Up @@ -956,7 +956,7 @@ std::string RealisticCamera::LensElementInterface::ToString() const {
curvatureRadius, thickness, eta, apertureRadius);
}

Float RealisticCamera::TraceLensesFromScene(const Ray &rCamera, Ray *rOut) const {
PBRT_CPU_GPU Float RealisticCamera::TraceLensesFromScene(const Ray &rCamera, Ray *rOut) const {
Float elementZ = -LensFrontZ();
// Transform _rCamera_ from camera to lens system space
const Transform LensFromCamera = Scale(1, 1, -1);
Expand Down
10 changes: 5 additions & 5 deletions src/pbrt/cameras.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,30 +584,30 @@ class RealisticCamera : public CameraBase {
pstd::vector<Bounds2f> exitPupilBounds;
};

inline pstd::optional<CameraRay> Camera::GenerateRay(CameraSample sample,
PBRT_CPU_GPU inline pstd::optional<CameraRay> Camera::GenerateRay(CameraSample sample,
SampledWavelengths &lambda) const {
auto generate = [&](auto ptr) { return ptr->GenerateRay(sample, lambda); };
return Dispatch(generate);
}

inline Film Camera::GetFilm() const {
PBRT_CPU_GPU inline Film Camera::GetFilm() const {
auto getfilm = [&](auto ptr) { return ptr->GetFilm(); };
return Dispatch(getfilm);
}

inline Float Camera::SampleTime(Float u) const {
PBRT_CPU_GPU inline Float Camera::SampleTime(Float u) const {
auto sample = [&](auto ptr) { return ptr->SampleTime(u); };
return Dispatch(sample);
}

inline const CameraTransform &Camera::GetCameraTransform() const {
PBRT_CPU_GPU inline const CameraTransform &Camera::GetCameraTransform() const {
auto gtc = [&](auto ptr) -> const CameraTransform & {
return ptr->GetCameraTransform();
};
return Dispatch(gtc);
}

inline void Camera::Approximate_dp_dxy(Point3f p, Normal3f n, Float time,
PBRT_CPU_GPU inline void Camera::Approximate_dp_dxy(Point3f p, Normal3f n, Float time,
int samplesPerPixel, Vector3f *dpdx,
Vector3f *dpdy) const {
if constexpr (AllInheritFrom<CameraBase>(Camera::Types())) {
Expand Down
4 changes: 3 additions & 1 deletion src/pbrt/cmd/imgtool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
#include <pbrt/filters.h>
#include <pbrt/options.h>
#ifdef PBRT_BUILD_GPU_RENDERER
#include <pbrt/gpu/denoiser.h>
#ifndef __HIP_PLATFORM_AMD__
#include <pbrt/gpu/optix/denoiser.h>
#endif // __HIP_PLATFORM_AMD__
#include <pbrt/gpu/util.h>
#endif // PBRT_BUILD_GPU_RENDERER
#include <pbrt/util/args.h>
Expand Down
Loading

0 comments on commit 5374fba

Please sign in to comment.